Runtime场景:
- 发送消息objc_msgSend
- 交换方法
- 动态添加方法
- 动态添加属性,属性关联
- 字典转模型
RunLoop使用场景 :
- 一、保证线程长时间存活
- 二、RunLoop如何保证NSTimer在视图滑动时依然能正常运转
- 三、RunLoop如何保证不影响UI卡顿
- 四、使用RunLoop 监测主线程卡顿,在主线程的RunLoop中添加一个observer,检测从 kCFRunLoopBeforeSources 到 kCFRunLoopBeforeWaiting 花费的时间是否过长。
- 五、监听kCFRunLoopBeforeWaiting,在RunLoop空闲时间内处理任务,比如下载图片
自动释放池
@autoreleasepool是一个由AutoreleasePoolPage组成的双向链表,在RunLoop启动时创建,在当前迭代停止时释放当前释放池内的对象。
首先执行objc_autoreleasePoolPush,向当前自动释放池插入一个哨兵对象,再将管理对象压入;如果自动释放池已满则新生成AutoreleasePoolPage并接在旧的后面。
释放时根据传入的哨兵对象地址找到哨兵对象所处的page,在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置。
主动释放,执行drain。
路由设计:
- 1、采用OpenURL方式(另外有Universal Links方式)
- 2、下发配置,启动时候注册
- 3、远程调用:解析URI,获取target,执行action
- 4、本地调用:获取target,执行action(performSelector的方式)
- 5、路由声明Protocol,服务方遵循后实现相关方法
- 6、默认全局以及自定义的容错处理方式
+load和+initialize:
| 说明 | +load | +initialize |
|---|---|---|
| 调用时机 | 被添加到 runtime 时 | 收到第一条消息前,可能永远不调用 |
| 调用顺序 | 父类load->子类load,主类load->分类load;不同分类之间的+load方法的调用顺序跟编译顺序有关 | 按消息传递机制:子类的分类->子类->父类的分类->父类 查找,找到则停止 |
| 调用次数 | 各自自身方法各调用1次 | 跟具体实现有关,如果子类与分类都未实现,那么父类的会调用多次 |
| 是否需要显式调用父类实现 | 否 | 否 |
| 是否沿用父类的实现 | 否 | 是(父子类都实现时需要区分不同实现) |
| 分类中的实现 | 类和分类都执行 | 覆盖类中的方法,只执行分类的实现 |
| 使用 | 一般用来实现 Method Swizzle | 一般用来初始化全局变量或者静态变量 |
| 实现 | 直接使用函数内存地址的方式 (*load_method)(cls, SEL_load)执行 | 使用发送消息 objc_msgSend 的方式 |
锁:
atomic 使用了原子性,能保证线程安全,其中用了自旋锁spinlock_t
1 NSLock 互斥锁
2 NSRecursiveLock 递归锁
3 NSCondition 特殊锁(线程直接的调度,两个线程直接下载图片与图片处理)
4 NSConditionLock 互斥锁与条件锁的组合,处理两个线程按特定顺序执行
5 pthread_mutex 互斥锁
6 pthread_rwlock 读写锁
7 POSIX Conditions 条件锁需要互斥锁和条件两项来实现
8 OSSpinLock 自旋锁(有优先级反转问题)
9 os_unfair_lock OSSpinLock等替代品(iOS10之后支持)
10 dispatch_semaphore 信号量
11 @synchronized 便捷的创建互斥锁的方式
NSLock < NSCondition < NSRecursiveLock < NSConditionLock
OSSPinLock < os_unfair_lock < gcd < pthread < NSLock < @synchronized
iOS 成员变量,实例变量,属性变量的区别:
成员变量是定义在{}号中的变量,如果变量的数据类型是一个类则称这个变量为实例变量。因为实例变量是成员变量的一种特殊情况,所以实例变量也是类内部使用的,无需与外部接触的变量,这个也就是所谓的类私有变量。而属性变量是用于与其他对象交互的变量。
- nonatomic<-->atomic(默认)
- readwrite<-->readonly(默认)
- retain/copy/assign
iOS等宽字体:
[UIFont fontWithName:@"HelveticaNeue" size:16];
内存区
-
栈区 内存管理由系统控制,存储的为非静态的局部变量,例如:函数参数,在函数中生命的对象的指针等。当系统的栈区大小不够分配时, 系统会提示栈溢出。
-
堆区 内存管理由程序控制,存储的为malloc , new ,alloc出来的对象。 如果程序没有控制释放,那么在程序结束时,由系统释放。但在程序运行过程中,会出现内存泄露、内存溢出问题。 分配方式类似于链表。
-
全局存储区(静态存储区) 全局变量、静态变量会存储在此区域。事实上全局变量也是静态的,因此,也叫全局静态存储区。 存储方式: 初始化的全局变量跟静态变量放在一片区域,未初始化的全局变量与静态变量放在相邻的另一片区域。 程序结束后由系统释放。
-
文字常量区 在程序中使用的常量存储在此区域。程序结束后,由系统释放。在程序中使用的常量,都会到文字常量区获取。
-
程序代码区 存放函数体的二进制代码。 运行程序就是执行代码,代码要执行就要加载进内存。
各种回调的同步异步
NSNotification、KVO、Delegate在哪个线程中触发,就在哪个线程中响应,而且都是同步的,会阻塞当前线程,直到处理完成。