gentle-lee / gentle-lee.github.io Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
MVC 作为使用最为广泛的架构模式,广泛的应用于各类场景
其中M代表Model, V代表View, C代表Control
而多数时候,我们会通过这么一个图表来表示它,
大致的意思是 : View传递指令给Controller, Controller负责业务逻辑的处理, 改动Model, Model再将改变渲染到视图上
听起来合理清晰简洁, 但是实际上,这只是最浅层的解释, 在实际编程中, 我们知道, 如果需要改变Model,View,Controller,那么我们需要持有他们的引用, 而持有引用又有造成内存泄漏的风险, 最终导致, 我们在controller中写下了大量的臃肿代码, 最终controller 真正的"C位出道",承担几乎所有的任务
而臃肿的代码是不适合迭代开发和单元测试的,所以后面会引出MVVM,MVP几种模式
最近在开发ios的TableView的时候, 需要多类型的Section 搭配多类型的列表项, 在medium上面看到了一篇使用MVVM模式来实现的文章
iOS: How to build a Table View with multiple cell types
可以说写的很不错, 首先它使用protocol(相当于interface)定义了一个DataTableModelItem, 然后让所有的列表项去满实现这个protocol, 从而可以通过多态向上转型成为一个TableItem, 这是一个非常好的封装的方式, 与设计模式中的Command模式类似, 然后它通过实现ViewModel来抽离TableView的dataSource, 从而让原本页面的controller不再那么臃肿
当时我觉得很强很厉害,屁颠屁颠的去实现了,但是后面发现, 如果你的代码不需要有太多的View和ViewModel的通信, 也就是这个列表如果仅仅是展示数据的话, 那么这是一个非常合适的方式
但是很遗憾,我实现的是一个列表的表单, 我需要从列表的表单中获取数据, 而我们从MVVM的图示中我们可以看到, View和ViewModel之间的箭头是双向的,但实际的编程中, 作为一个swift开发的新手, 我发现这么做很困难, 最终我是通过 " 闭包传值 "来实现的这一个方式, 在使用MVVM的过程中, 我对组件间的通信充满了疑惑, 因为为了达到通信的作用,我不得不在各处放置引用和闭包, 在帮助我减少耦合的过程中,我又担惊受怕的担心相互引用会造成内存泄漏,问题来了: 在解耦的同时, 各部分的数据通信应该遵循什么规则?
MVP模式, 仍然作为一种解耦的架构模式实现, 主要优点在于每个presenter可以有单元测试
在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。 ----维基百科
简而言之,闭包 是 “函数” 和 “变量作用域” 建立关系的一种方式,例如:在 A 函数里面返回 B 函数,然后 B 函数里面一直引用着 A 函数的布局变量。此时,我们在别处使用A函数中返回的B函数的时候,B函数中可以访问A函数中的变量, 这就是闭包
用代码来解释吧
function A(){
var i=0;
function B(x){
i = x;
}
return B;
}
var c=a();
c(3);//此时,i的值被改变了
这里推荐一个关于闭包的讨论, 建议进去里面涮一涮
此时你可能还不懂,不要着急,我们通过原理和应用两个方面来理解闭包
闭包的原理就是变量的作用域,如果你分不清楚静态作用域和动态作用域之间的区别,建议翻阅 "静态作用域和动态作用域 #2“
其实闭包的本质是静态作用域。因为 JavaScript 没有动态作用域,所以函数访问的都是定义时的作用域,所以闭包才得以实现。
而闭包能够使得变量的作用域变广,这其实也是闭包的实际作用之一
而有人要问了,为什么不直接把变量放到全局变量中,这其实和我们为什么要设置private和public的道理是一样的,我们需要限制变量的访问以防止程序变量被意外(恶意)修改
闭包应该怎么用? 1. 传递数据,2. 封装接口
上面链接中有一位答主回答的我觉得很不错,也结合了实际的应用来解释
简单来说,闭包是指当函数被当成对象返回时,如果夹带了外部变量就形成了闭包。
如果一个函数打包了外部变量,就可以给程序非常大的灵活性,你可以把闭包理解成轻量级的接口封装,虽然对外都是这个函数(调用方式不变),但是因为之中的变量不一样,就可以完成很多功能。这也就是那位同学说的自带运行环境的函数,自带背景音乐的男人,想想都可怕。
这里把闭包的概念变为:函数被当成对象返回,并夹带外部变量,这其实没有矛盾,这只是一个结合了实际运用的说法罢了,有助于我们理解
至于例子嘛,可以参照这里的说说 JavaScript 的闭包用于什么场景
##回调
闭包的概念还是很难理解的,这个时候再扯进来回调,在Java中,在多线程的通信中,我们有时候也会使用回调来获取其他类中的变量喔,那么,回调是不是就是闭包呢?
实际上,并不是,有时候我们要理解,在编程上面,虽然有些东西在实现和功能上可能几乎完全一样,但是他们的**不同 ,像设计模式中就有过不少,在实现上几乎没啥区别的,但是不是同一种设计模式的(此处一时不能想起很合适的例子,暂且略过。。)
回调虽然和闭包是在实现上和功能上类似,但是,闭包我们知道了,更倾向于拓宽变量的作用域或类似接口封装
那什么是回调呢?是在程序里面留空, 举一个知乎上的例子, 酒店有叫醒服务,但是怎么叫醒的由我们决定,这里,叫醒的方式就是回调函数,我们需要自己定义然后传给酒店,等待酒店帮我们执行
我们可以在回调函数中做任何我们可以做的事情,这当然也就包括访问我们自己的变量(从而达到和闭包一样的效果)
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
这道题目看起来像是一道搜索题目, 一般的搜索题目我们可以采用排序然后二分查找或者双指针的方法来解决,但是这道题目要求返回数组的索引, 而一旦我们排序了, 那么索引也就被打乱了, 并且经过尝试, 基本上往这个思路靠拢的代码写起来都是不美观的
那么我们该怎么做, 首先是分析题目, 题目说 "和为目标值", 那么也就说明
nums[x] + nums[y] = target
nums[y] = target - nums[x]
也就是这两个数之间是存在关系的, 我们需要好好的利用这个关系, 这样我们就可以在遍历的时候通过这个关系找到另一个数, 其中一种方式就是: 我们保存nums[i] 和 i , 使用HashMap, nums[i] 为key, i 为值
这样的话, 我们可以通过hashmap 来判断关系
int diff = target - nums[i];
if (hashmap.contains(diff)){
return new int[]{i,hashmap.get(diff)};
}
public class Solution {
public int[] twoSum(int[] numbers, int target) {
HashMap<Integer,Integer> hash = new HashMap<Integer,Integer>();
for(int i = 0; i < numbers.length; i++){
Integer diff = (Integer)(target - numbers[i]);
if(hash.containsKey(diff)){
int toReturn[] = {hash.get(diff), i};
return toReturn;
}
hash.put(numbers[i], i);
}
return null;
}
}
数组类的题目大部分是需要使用排序算法的**, 但是做算法题目,锻炼的更主要是分析能力, 不要被算法蒙蔽了头脑
在了解闭包的过程中遇到了静态作用域和动态作用域,概念不太清楚,遂查之
看到了一篇很不错的博文 浅谈静态作用域和动态作用域,做个笔记
int x = 1;
int g(int z) { return x + z; }
int f(int y)
{
int x = y + 1;
return g(y*x);
}
f(3);
静态作用域和动态作用域的一个重要区别在于:静态作用域规则查找一个变量声明时依赖的是源程序中块之间的静态关系;而动态作用域规则依赖的是程序执行时的函数调用顺序。说的具体点,就是静态作用域查找的是距离当前作用域最近的外层作用域中同名标识符的声明,而动态作用域则是查找最近的活动记录(图一)
此时控制栈中有两个名为x的变量,一个在最外层作用域中定义,另一个是函数f的局部变量。在采用动态作用域规则的情况下,表达式x+z中的x会从最近的活动记录中寻找变量x的值,也即函数调用f(3)的活动记录中的x值,则x的值4;而如果采用静态作用域规则,表达式x+z中的变量x则从函数g定义所在作用域的外层作用域中寻找,此时x的值为1。
A stack based solution for reference, inspired by Histogram
这个答案的思路是通过另一个答案来的,总结了相同的方法和思路,告诉我们当我们遇到类似的问题应该如何解决,值得学习
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").
输入: s1= "ab" s2 = "eidboaoo"
输出: False
输入的字符串只包含小写字母
两个字符串的长度都在 [1, 10,000] 之间
我们很多时候,都会先使用遍历的**去思考问题,像这道题目,我们第一想法可能就是考虑怎么得到所有字符串的排列情况,通过字符串的排列组合情况去遍历,这种思考起来是比较困难的
除了遍历的**,我们应该还要有推导的想法,比如这道题目,s2含有s1的所有排列,说明
而示例2表明,如果我们只是单纯的考虑s2中包含所有s1的字符,那么我们就会出错,题目的本意是s2的子串(也就是连续的字符串)是s1 的排列,这也就是推导的想法:如果s2中有子串由s1中的字符构成,那么s2中有s1的所有排列。
所以问题转换为了,判断 s1长度的滑动窗口在s2上的是否含有s1的全部字符
class Solution {
public boolean checkInclusion(String s1, String s2) {
if (s1.length() > s2.length())
return false;
//共有26个英文字符
int[] count = new int[26];
//将滑动窗口s1置于s2开头,计算s1和s2之间的区别(体现在count数组中)
for (int i = 0 ;i < s1.length(); i ++){
count[s1.charAt(i) - 'a'] ++;
count[s2.charAt(i) - 'a'] --;
}
/*
count数组表明滑动窗口s1和s2的差异,如果count数组全部为零,说明此时滑动窗口s1和s2中的子串无差异
*/
if (checkIfAllZero(count))
return true;
/*
开始滑动,因为count数组表示的是差异,所以向右滑动的时候,我们应该恢复s2左边(加回来),加入s2右边(减去)
*/
for (int i = s1.length(); i < s2.length(); i ++) {
count[s2.charAt(i) - 'a']--;
count[s2.charAt(i - s1.length()) - 'a']++;
if (checkIfAllZero(count))
return true;
}
return false;
}
boolean checkIfAllZero(int[] count){
for (int val : count){
if (val != 0)
return false;
}
return true;
}
}
git branch -r
git checkout -b [localBranchName] origin/[remoteBranchName]
这条命令会自动创建并且切换到该本地分支,并建立远程分支和本地分支的联系
Branch 'localBranchName' set up to track remote branch 'remoteBranchName' from 'origin'.
Switched to a new branch 'localBranchName'
有时候,我们需要将其他项目(网站)的分支推送到github的某一个项目上,我们只需要
git push [email protected]:userName/projectName.git branchName
常见的使用情况有:将bitbucket,coding上的项目转移到github上
git checkout .
rm file
git clean -xdf //所有文件夹
git reset HEAD filename
git reset commit_id
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.