-
多线程
每个iOS应用程序都有个专门用来更新显示UI界面、处理用户的触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验。一般的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程编程是防止主线程堵塞,增加运行效率的最佳方法。
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- GCD
* GCD是Grand Central Dispatch的简称,它是基于C语言的。如果使用GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue)。GCD会负责创建线程和调度你的任务,系统直接提供线程管理。
* GCD的一个重要概念是队列,它的核心理念:将长期运行的任务拆分成多个工作单元,并将这些单元添加到dispath queue中,系统会为我们管理这些dispath queue,为我们在多个线程上执行工作单元,我们不需要直接启动和管理后台线程。
* 1> 使用dispatch_get_current_queue函数作为调试用途,或者测试当前queue的标识。在block对象中调用这个函数会返回block提交到的queue(这个时候queue应该正在执行中)。在block对象之外调用这个函数会返回应用的默认并发queue。
2> 使用dispatch_get_main_queue函数获得应用主线程关联的串行dispatch queue
3> 使用dispatch_get_global_queue来获得共享的并发queue
* 它具有以下优点:
1、易用: GCD比之thread跟简单易用。由于GCD基于work unit而非像thread那样基于运算,所以GCD可以控制诸如等待任务结束、监视文件描述符、周期执行代码以及工作挂起等任务。基于block的血统导致它能极为简单得在不同代码作用域之间传递上下文。
2、效率: GCD被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。这关系到易用性:导致GCD易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了。
3、性能: GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。
4、GCD可以将花费时间极其长的任务放到后台线程,可以改善应用的响应性能
5、GCD 提供一个易于使用的并发模型而不仅仅只是锁和线程,以帮助我们避开并发陷阱
6、GCD 具有在常见模式(例如单例)上用更高性能的原语优化你的代码的潜在能力
http://blog.csdn.net/charles91/article/details/50542940
* 并发队列:(ConcurrentDispatch Queue)
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
注意:并发功能只有在异步(dispatch_async)函数下才有效,因为异步函数才具备开启新线程的能力,而同步函数只能在当前线程中执行不具备开启线程的能力。
dispatch_queue_t queue = dispatch_queue_create("队列名称", DISPATCH_QUEUE_CONCURRENT);
GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建,使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);```
* 串行队列:(SerialDispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
注意:使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列
GCD中获得串行有2种途径
1、使用dispatch_queue_create函数
//创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_tqueue = dispatch_queue_create("队列名称",NULL);
2、主队列(是GCD自带的一种特殊的串行队列)
放在主队列中的任务,都会放到主线程中执行
dispatch_queue_tqueue = dispatch_get_main_queue();
* dispatch_async:异步任务, 可以在新的线程中执行任务,具备开启新线程的能力
dispatch_async:同步任务,只能在当前线程中执行任务,不具备开启新线程的能力
要使用用户队列,我们首先得创建一个。调用函数dispatch_queue_create就行了。
调用dispatch_async函数,传入一个队列和一个block。队列会在轮到这个block执行时执行这个block的代码。下面的例子是一个在后台执行一个巨长的任务:
* dispatch_group_t: 队列组
应用场景:分别异步执行2个耗时的操作,要等2个异步操作都执行完毕后,再回到主线程执行操作
注意:该场景,使用上面的栅栏也可解决
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
//执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(),^{
//等前面的异步操作都执行完毕后,回到主线程...
});
* dispatch_barrier_async:栅栏
场景:1.拦截前面的任务, 只有先添加到队列中的任务=执行完毕, 才会执行栅栏添加的任务
2.如果栅栏后面还有其它的任务, 那么必须等栅栏执行完毕才会执行后面的其它任务
注意:1.如果想要使用栅栏, 那么就不能使用全局的并发队列
2.如果想使用栅栏, 那么所有的任务都必须添加到同一个队列中
* dispatch_after延时执行。
dispatch_after 工作起来就像一个延迟版的 dispatch_async 。你依然不能控制实际的执行时间,且一旦 dispatch_after 返回也就不能再取消它。
何时适合使用 dispatch_after ?主队列(串行):是使用 dispatch_after 的好选择。
* dispatch_once:
1、对于某个任务执行一次,且只执行一次。 dispatch_once函数有两个参数,第一个参数predicate用来保证执行一次,第二个参数是要执行一次的任务block。
2、dispatch_once被广泛使用在单例、缓存等代码中,用以保证在初始化时执行一次某任务。
3、dispatch_once在单线程程序中毫无意义,但在多线程程序中,其低负载、高可依赖性、接口简单等特性,赢得了广大消费者的一致五星好评。
* dispatch_apply:
使用dispatch_apply函数能进行快速迭代遍历,但是它和for循环又有些不同
第一个参数: 需要遍历几次
第二个参数: 决定第三个参数的block在哪个线程中执行
第三个参数: 回掉
dispatch_apply(10, 队列queue, ^(size_tindex){
//执行10次代码,index顺序不确定
});
*实例:拷贝文件到目标目录中 : 快速迭代实现*
2. NSOpration
1、简单说明
NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现多线程编程
添加操作到NSOperationQueue中,自动执行操作,自动开启线程
NSOperation和NSOperationQueue实现多线程的具体步骤:
(1)先将需要执行的操作封装到一个NSOperation对象中
(2)然后将NSOperation对象添加到NSOperationQueue中
(3)系统会⾃动将NSOperationQueue中的NSOperation取出来
(4)将取出的NSOperation封装的操作放到⼀条新线程中执⾏
2、NSOperation的子类
NSOperation是个抽象类,并不具备封装操作的能力,必须使⽤它的子类
使用NSOperation⼦类的方式有3种:
(1)NSInvocationOperation
(2)NSBlockOperation
(3)自定义子类继承NSOperation,实现内部相应的⽅法
3、功能
(1)并发数:同时执⾏行的任务数,可设置最大并发数。
(2)队列的取消,暂停和恢复
(3)设置NSOperation在queue中的优先级,可以改变操作的执⾏优先级
(4)NSOperation之间可以设置依赖来保证执行顺序,⽐如一定要让操作A执行完后,才能执行操作B,可以像下面这么写
[operationB addDependency:operationA]; // 操作B依赖于操作
(5)可以监听一个操作的执行完毕
//监听操作的执行完毕
operation.completionBlock=^{
//.....下载图片后继续进行的操作
NSLog(@"--接着下载第二张图片--");
};
[operation setCompletionBlock:^() {
NSLog(@"执行完毕");
}];
3. NSThread
是三种方法里面相对轻量级的,但需要管理线程的生命周期、同步、加锁问题,这会导致一定的性能开销
1.优点:NSThread比其他两种多线程方案较轻量级,更直观地控制线程对象
2.缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
* **造成循环引用的原因和解决方法,delloc方法不执行**
1. NSTimer的循环引用的原因和解决方法
2. Block原理
3. 代理
4. 自定义封装的函数中传入了当前类
详情见该文章:[内存泄露控制器不释放造成dealloc方法不执行的原因全解](http://www.jianshu.com/p/0b19c6cb8fc4);
* **runtime机制**
runtime是一套底层的C语言API,包含很多强大实用的C语言数据类型和C语言函数,平时我们编写的OC代码,底层都是基于runtime实现的。
* 功能用途:
1.动态的遍历一个类的所有成员变量,用于字典转模型,归档解档操作
2.可以动态的添加、修改、删除类、方法、属性,方法交换。
* 消息机制:方法调用称为向对象发送消息
* 一个OC方法被编译成objc_msgSend,OC的对象通过isa找到类对象
* 类对象查找自己存储的方法列表来找到对应的方法执行体
* 方法执行体执行具体的代码,并返回值给调用者。
* 子类调用一个方法,如果子类没有实现,会查找基类。
* 消息转发(可以间接实现多重继承)
当向someObject发送某消息,但runtime system在当前类和父类中都找不到对应方法的实现时,runtime system并不会立即报错使程序崩溃,而是依次执行下列步骤:
1.动态方法解析:向当前类发送 resolveInstanceMethod: 信号,检查是否动态向该类添加了方法。(迷茫请搜索:@dynamic)
2.快速消息转发:检查该类是否实现了 forwardingTargetForSelector: 方法,若实现了则调用这个方法。若该方法返回值对象非nil或非self,则向该返回对象重新发送消息。
3.标准消息转发:runtime发送methodSignatureForSelector:消息获取Selector对应的方法签名。返回值非空则通过forwardInvocation:转发消息,返回值为空则向当前对象发送doesNotRecognizeSelector:消息,程序崩溃退出。
* **runloop机制**
Runloop是个事件循环,是事件接收和分发机制的一个实现。内部是一个 do-while 循环。保证程序执行的线程不会被系统终止。
主线程��默认有Runloop。当自己启动一个线程,如果只是用于处理单一的事件,则该线程在执行完之后就退出了。
* **视频的上传、下载,视频流**
* **性能、内存优化**
1. ARC、MRC
2. 优化方案
详情见[iOS 性能优化最全总结](http://www.jianshu.com/p/59bd44b37199);
* **oc与js通信原理**
* **MVC、MVVM**
详情见[被误解的MVC和被神化的MVVM](http://www.cnblogs.com/jifeng/p/5095126.html);
* **数据持久化**
1. NSUserDefaults
2. CoreData
3. 文件缓存:plist
4. 归档、解档
5. FMDB
* **masonry机制 学到什么思想**
* **HTTP、TCP、Socket网络通信**
TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。
UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快。
TCP是一种流模式的协议,UDP是一种数据报模式的协议。
Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
详情见[socket详解](http://www.jianshu.com/p/3e4f3de18e3b#)
* **设计模式**
(一)代理模式
应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
优势:解耦合
敏捷原则:开放-封闭原则
实例:tableview的 数据源delegate,通过和protocol的配合,完成委托诉求。
列表row个数delegate
自定义的delegate
(二)观察者模式
应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息。
优势:解耦合
敏捷原则:接口隔离原则,开放-封闭原则
实例:Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。
kvo,键值对改变通知的观察者,平时基本没用过。
(三)MVC模式
应用场景:是一中非常古老的设计模式,通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。
优势:使系统,层次清晰,职责分明,易于维护
敏捷原则:对扩展开放-对修改封闭
实例:model-即数据模型,view-视图展示,controller进行UI展现和数据交互的逻辑控制。
(四)单例模式
应用场景:确保程序运行期某个类,只有一份实例,用于进行资源共享控制。
优势:使用简单,延时求值,易于跨模块
敏捷原则:单一职责原则
实例:[UIApplication sharedApplication]。
注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。
java,C++中使其没有公有构造函数,私有化并覆盖其构造函数。
object c中,重写allocWithZone方法,保证即使用户用 alloc方法直接创建单例类的实例,
返回的也只是此单例类的唯一静态变量。
(五)策略模式
应用场景:定义算法族,封装起来,使他们之间可以相互替换。
优势:使算法的变化独立于使用算法的用户
敏捷原则:接口隔离原则;多用组合,少用继承;针对接口编程,而非实现。
实例:排序算法,NSArray的sortedArrayUsingSelector;经典的鸭子会叫,会飞案例。
注意事项:1,剥离类中易于变化的行为,通过组合的方式嵌入抽象基类
2,变化的行为抽象基类为,所有可变变化的父类
3,用户类的最终实例,通过注入行为实例的方式,设定易变行为
防止了继承行为方式,导致无关行为污染子类。完成了策略封装和可替换性。
(六)工厂模式
应用场景:工厂方式创建类的实例,多与proxy模式配合,创建可替换代理类。
优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。
敏捷原则:DIP依赖倒置原则
实例:项目部署环境中依赖多个不同类型的数据库时,需要使用工厂配合proxy完成易用性替换
注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,
增 加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。
* **UI的渲染机制和动画原理**
* **第三方库的实现原理**
1. MJRefresh
2. JsonModel
3. MJExtention
4. SDWebimage