UI试图相关问题
大纲
- UITableView 相关
- 事件传递&视图响应
- 图像显示原理
- 卡顿&掉帧
- 异步绘制&绘制原理
- 离屏渲染
重点:
1、重用机制
2、并发访问,更新数据(1、子线程同步主线程的操作2、多线程操作同步到串行队列执行)
3、传递和响应(传递由上到下,响应由下往上)
4、图像显示原理
- CPU 处理布局,绘制,图像编解码,提交位图
- GPU 渲染管线(顶点着色,图元装配,光栅化,片段着色,片段处理)
5、处理UI卡顿掉帧的问题
- 原因 VSync信号来临时,GPU中不能提交相应画面数据(未处理完)
6、异步绘制 实现了layer的delegate方法,即可进行异步绘制displayLayer
Object-C特性
大纲
- 分类
- 关联对象
- 扩展
- 代理
- 通知
- KVO
- KVC
- 属性关键字
重点
分类:
- 运行时决议
- 可以为系统类添加分类
- 添加 1、实例方法 2、 类方法 3、协议 4、属性
- 最后编译的分类方法优先生效
- 分类添加的方法“覆盖”原类方法
- 名字相同的分类会引起编译报错
关联兑现:
- 为分类所添加的成员变量不会被添加到宿主类上,而是关联到AssocitationsManager管理(HashMap)
扩展:
- 用扩展生命私有属性
- 用扩展生命私有方法
- 用扩展生命私有成员变量
- 编译时决议
- 只以声明的形式存在
- 不能为系统类添加扩展
代理
- 软件设计模式
- @protocol
- 一对一
通知:
- 一对多
- 使用观察者模式实现,用于跨层传递消息的机制
KVO
- 观察者模式的一种实现
- isa混写技术来实现KVO(修改监听对象的isA指针的指向)
- 手写KVO valueWillChanged、valueDidChanged
KVC
- key value coding
- 先判断有没有属性方法,如果没有,则判断有无实例变量,如也没有则报错
属性关键字
- atomic
- nonatomic
- assign/week
- copy
Runtime
大纲
- 数据结构
- 类对象与原类对象
- 消息传递
- 方法缓存
- 消息转发
- Method-Swizzling
- 动态添加方法
- 动态方法解析
数据结构
- objc_object
结构体: isa_t、关于isa操作相关、弱引用相关、关联对象相关、内存管理相关 - objc_class
结构体:suoerClass指针、cache_t cache(方法缓存)、class_data_bits_t(只读信息,协议,属性,方法) - method_t
名称,返回值,参数,函数体
const char* types => Type Encodings
返回值 参数1 参数2 ... 参数n
只读信息中:name 原生方法列表 成员变量 属性 协议 - 对象、类对象、原类对象(关系)
消息传递
void objc_mgsSend(Void /* id self, SEL op, .../)
void objc_mgsSendSuper(Void / struct objc_super super, SEL op, .../)
消息转发流程
resolveInstanceMethod: 返回YES 消息处理结束 ,返回NO forwardingargetForSelector: 返回转发目标, 返回nil methodSignatureForSelector: 返回方法签名,返回nil 报错
Method-Swizzling
- 方法交换
动态添加方法
- class_addMethod: 接受对象,函数名,方法签名
动态方法解析
- @dynamic
内存管理
大纲
- 内存布局
- 内存管理方案
- 数据结构
- ARC & MRC
- 引用计数
- 弱引用
- 自动释放池
- 循环引用
内存布局
- 栈(向下增大)
- 堆(向上增大)
- 未初始化数据
- 已初始化数据
- 代码段
内存管理方案
iOS是怎样管理内存的?
- TaggedPointer
- NONPOINTER_ISA (非指针型isa)
- 散列表(弱引用表和引用计数表)
NONPOINTER_ISA
散列表
- SideTavles() 结构 ,是一个哈希表,是一个多张表,可以实现分离锁
怎样实现快速分流
使用hash表查找,提高查找效率
数据结构
散列表的数据结构
- Spinlock_t 自旋锁
- RefcountMap 引用计数表
- weak_table_t 弱引用表
Spinlock_t
- 是一个“忙等”的锁。
- 适用于轻量访问
RefcountMap
- 是一个hashMap
- 使用hash查找为了提高引用效率
weak_table_t
- 是一个hashMap
- 存储了弱引用对象的指针
MRC
手动引用计数
- alloc 分配内存空间
- retain 引用计数加一
- release 引用计数减一
- retainCount 获取对象的引用计数
- autorelease 在autoreleasePool结束的时候调用release
- dealloc 调用super dealloc
ARC
自动引用
- ARC是LLVM和Runtime协作的结果
- ARC新增了weak、strong属性关键字
引用计数管理
alloc实现
调用了c函数的calloc
此时并没有增加retainCount为1
retain
- 查找对象的SideTable表
- 从SideTable表中获取当前对象的引用计数值
- 对引用计数值进行+1操作
release
- 查找SideTable
- 从SideTable表中获取当前对象的引用计数值
- 对引用计数值进行-1操作
retainCount
- 查找SideTable
- 从SideTable表中获取当前对象的引用计数值
- 若不存在引用计数,则对引用计数值进行+1操作
dealloc
当前对象是否可以直接释放一句以下判断条件
- nonpointer_isa
- weakly_referenced
- has_assoc 是否有关联对象
- has_cxx_dtor 是否有C++内容,或是否使用arc管理内存
- has_sidetable_rc 当前对象的引用计数是否通过sidetable表维护的
以上全部为否才可以调用C函数直接释放
否则就要调用object_dispose() 进行释放
object_dispose
- 开始
- objc_destructInstance(): c++释放、移除关联对象、将弱引用指针置位nil、清除引用计数
- c函数free()
- 结束
弱引用管理
添加弱引用变量的流程
- objc_initWeak()
- storeWeak()
- weak_register_no_lock()
1 通过对象指针hash计算查找
2 如果已经存在了弱引用数组,则添加
3 如果没有,则创建弱引用数组
清除weak变量,同事设置为nil
- dealloc
- 。。。
- weak_clear_no_lock()
自动释放池
- runloop将要结束时调用pop操作
- 多层嵌套就是多次插入哨兵对象
- 在for循环中alloc创建了较大的内存消耗是,可手动插入autoReleasePool来释放内存对象
循环引用
- 自循环引用
- 相互循环应用
- 多循环引用
考点
- 代理
- Block
- NSTimer
- 大环引用
如何破除
- 避免产生
- 在合适的时机手动破除循环引用
__weak
__block(ARC下会被强引用)
__unsafe_unretained 修饰对象不会增加引用计数,但是会产生悬垂指针
解决NSTimer的循环引用问题
NSTimer会被Runloop引用,所以必须手动释放NSTimer来解除引用。
采用中间对象,同时弱引用NSTimer和对象,当对象被释放后,NSTimer回调后,判断弱引用对象已经释放为nil,此时则invalidate timer,将NSTimer置位nil,此时NSTimer也被成功释放。
Block
大纲
- Blokc介绍
- 截获变量
- __block修饰符
- Block的内存管理
- Block的循环引用
Block = 函数 + 上下文 + 对象
Block截获变量
Block
- 全局类型block _NSConcreteGlobalBlock (堆)
- 栈类型block _NSConcreteStackBlock (栈)
- 堆类型block _NSConcreteMallocBlock (已初始化数据区)
栈:拷贝 -》 堆:拷贝-》 堆(引用计数器+1)
注意:
使用了__block 修饰的的截获变量修改,会对block进行copy
多线程
大纲
- GCD
- NSOperation
- NSThread
- 多线程与锁
GCD
- 同步/异步 和 串行/并发
- dispatch_barrier_aysnc
- dispatch_group
同步/异步 和 串行/并发
1.1 dispatch_sync(serial_queue /串行队列/,, ^{ // 任务 });
1.2 dispatch_async(serial_queue, ^{ // 任务 });
1.3 dispatch_sync(concurrent_queue /并发队列/, ^{ // 任务 });
1.4 dispatch_async(concurrent_queue, ^{ // 任务 });dispatch_barrier_async
2.1 如何实现多读单写
dispatch_barrier_async(concurrent_queue, ^{ // 写操作 });dispatch_group
3.1 A,B,C三个任务完成后,再执行D
NSOperation
- NSOperationQueue
1.1 可以添加任务依赖
1.2 可以添加任务执行状态
1.3 可以控制最大并发量
任务执行状态
- isReady
- isExcuting
- isFinished
- isCancelled
状态控制
- 只重写main时,则无法控制状态
- 重写了start,自行控制任务状态
通过KVO来监听NSOperation状态
NSThread
- 如何实现常驻线程
- Start方法内部实现机制(开启线程,执行函数,关闭线程)
多线程和锁
- NSRecursiveLock
- NSLock
- dispatch_semaphore_t
@synchronized
单例对象
atomic
原子性赋值
OSSpinLock
自旋锁(循环等待访问,不释放当前资源)
NSLock
NSRecursiveLock
递归锁,可以重注
dispatch_semaphore_t
信号量
RunLoop
大纲
- 概念
- 数据结构
- 事件循环机制
- RunLoop与NSTimer
- RunLoop与多线程
什么是RunLoop
RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象
- 维护事件循环
- 处理事件和消息的
- 对象
- 事件循环
- 没有消息处理时,休眠以避免资源占用
- 有消息需要处理时,立刻唤醒
- main函数中,开启了运行循环,保证函数不会被结束,等待 ≠ 死循环
数据结构
- NSRunLoop => Foundation
- CFRunLoop => CoreFoundation
CFRunLoop:
- name: 某一个runloopMode的名称,通过名称来找到模式
- sources0: CFRunLoopSource => source0 手动唤醒
- sources1: source1 具备唤醒线程的能力
- observers: 观测时间点
- timers:runloop的定时器
1 RunLoop -> n Model:
- m Source
- m Timer
- m Observer
事件循环的实现机制
- 即将进入RunLoop
- 将要处理Timer/Source0事件
通知Observer - 处理Source0事件
- 如果有Source1要处理 -> 8. 处理唤醒时收到的消息
- 线程将要休眠
- 休眠、等待唤醒
6.1 Source1
6.2 Timer事件的回调
6.3 外部手动唤醒 - 线程刚被唤醒
- 即将退出RunLoop
RunLoop的核心
RunLoop与NSTimer
NSCommonMode
RunLoop与多线程
- 线程和RunLoop是一一对应的
- 新建的线程默认是没有RunLoop的
怎样实现一个常驻线程
- 创建一个RunLoop
- 添加port/source给RunLoop
- 启动RunLoop
网络
大纲
HTTP协议
HTTPS与网络安全
TCP/UDP