目录
多线程
- AutoLayout
- objc_msgSend
- Runtime
消息转发
Category
NSObject 与 objc_class
- Runloop
- AutoreleasePool
iOS 系统架构
App 启动过程和优化
UIScrollView 的代理方法
响应链和事件传递
UIView 和 CALayer 的区别和联系
轮播图朴素实现的几种方法
TableView 和 CollectionView 必选的代理方法
UITableView 的优化思路
多线程
线程之间同步
原子操作 Atomic
加锁(互斥锁, 递归锁, 读写锁)NSLock,OSSpinLock
多线程之间通信
performSelectorOnMainThread:withObject:waitUntilDone:
如何保证线程安全
OSSpinLock 自旋锁
dispatch_semaphore
NSLock 等各种锁
@synchronized
多线程的坑
常驻线程
常驻线程多了影响 CPU 效率
AFNetworking2.0 因为用的 NSURLConnection 有缺陷, 需要所在线程一直存活, 所以保持了个常驻线程, 3.0 用了 NSURLSession, 可以指定回调的 delegateQueue 于是弃用常驻线程.
[runloop run]是常驻线程,[runloop runUntilDate]指定保活时长
并发
GCD 本着最大化 CPU 效率的原则会多创建线程, 但如果是 IO 类操作, 需要等待数据的空档会继续创建新线程导致内存失控. 类似数据库操作尽量用串行队列避免多线程并发导致问题. 因为创建线程需要堆栈内存, 切换线程也消耗 CPU.
死锁
串行队列 (如主队列) 同步操作
AutoLayout
更新屏幕时, Layout Engine 从上到下调用 layoutSubviews()通过 Cassowary 算法计算各个子视图的位置, 算出来后将子视图的 frame 从 Layout Engine 里拷贝出来, 接下来的过程就跟手写 frame 是一样的了. iOS12 优化了性能, 以前元素多了会导致性能下降, 现正不会了
objc_msgSend
检查这个 selector 是不是要忽略的
检查 target 是不是 nil
如果有相应处理 nil 的函数就跳转到该函数
如果没有就自动清理现场并返回, 这就是 OC 中给 nil 发消息不会崩溃的原因
确定不是给 nil 发消息后, 在该 class 的缓存中查找方法对应的 IMP 实现
如果找到就跳转进去
如果没找到就在方法分发表里继续查找, 直到找到 NSObject 为止
如果还没找到就开始消息转发, 上述过程就是通过 SEL 快速查找 IMP 的过程
Runtime
C 语言中, 编译期函数的调用就决定调用哪个函数, 而 OC 只有在真正运行时才根据函数名称找到对应函数来调用. 需要一个运行时系统来动态地创建类和对象, 消息传递和转发
讲一下 OC 的消息机制
OC 中的方法调用其实都是转成了 objc_msgSend 函数的调用, 给 receiver(方法调用者)发送了一条消息(selector 方法名)
objc_msgSend 底层有 3 大阶段: 消息发送(当前类, 父类中查找), 动态方法解析, 消息转发
当递归地找不到 selector 时, 启动消息转发: resolveInstanceMethod,resolveClassMethod,forwardingTargetForSelector
什么是 Runtime? 平时项目中有用过么?
OC 是一门动态性比较强的编程语言, 允许很多操作推迟到程序运行时再进行
OC 的动态性就是由 Runtime 来支撑和实现的, Runtime 是一套 C 语言的 API, 封装了很多动态性相关的函数
平时编写的 OC 代码, 底层都是转换成了 Runtime API 进行调用
Runtime 的应用(优点):
实现多继承 Multiple Inheritance
Method Swizzling -> 无侵入埋点 -> 使用 Category 进行
Aspect Oriented Prigramming 面向切面编程 AOP -> 如日志, 身份验证, 缓存等模块
isa Swizzling -> KVO 的实现
Associated Object 关联对象(给 Category 添加属性) objc_set(get)AssociatedObject
动态地增加方法
NSCoding 的自动归档和自动解档
字典和模型相互转换!
异常保护(保护数组越界问题)
Runtime 的缺点
Method Swizzling 不是原子操作, 放在 + load 里没问题, 放在 + initialize 里就有问题了
重写方法而不调用 super 方法可能有问题
Runtime 注意事项
Swizzling 应该总在 + load 中执行
Swizzling 应该总在 dispatch_once 中执行 -> 因为会改变全局状态所以应该只执行一次
+load 中执行 Swizzling 时, 不要调用[super load] -> 避免偶数次执行 Swizzling
SEL 其本身是一个 Int 类型的地址, 地址中存放着方法的名字.
Method Swizzling
几个常见方法
method_setImplementation
为一个方法名设置 IMP(实现)
method_exchangeImplementations
交换两个方法名的实现, 即执行两次 method_setImplementation
class_addMethod
根据官方注释解释, 这个方法用于给指定的类增加方法名和 IMP(实现), 如果该已经存在这个方法名, 不做事, 返回 NO, 如果该类不存在这个方法名(即使父类存在), 添加这个方法, 返回 YES
class_replaceMethod
根据官方注释解释, 它有两种不同的行为. 当类中没有想替换的原方法时, 该方法会调用 class_addMethod 来为该类增加一个新方法. 若已存在, 则等同于 method_setImplementation 为该方法名替换 IMP(实现)
Swizzling 模板
- + (void)hookClass:(Class)classObject fromSelector:(SEL)fromSelector toSelector:(SEL)toSelector {
- Class class = classObject;
- Method fromMethod = class_getInstanceMethod(class, fromSelector);
- Method toMethod = class_getInstanceMethod(class, toSelector); // 得到交换类的实例方法
- if(class_addMethod(class, fromSelector, method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
- class_replaceMethod(class, toSelector, method_getImplementation(fromMethod), method_getTypeEncoding(fromMethod)); // 进行方法的交换
- } else {
- method_exchangeImplementations(fromMethod, toMethod); // 交换 IMP 指针
- }
- }
- + (void)load {
- static
来源: http://www.bubuko.com/infodetail-3331968.html