1. 内存布局
stack: 栈区, 方法调用. (高地址)heap: 堆区, 通过alloc等分配的对象bss: 未初始化的全局变量等data: 已初始化的全局变量等text: 程序代码
2. 内存管理方案
系统方案
TaggedPointer(小对象: 如NSNumber)NONPOINTER_ISA(非指针型isa, arm64架构程序)- 散列表(
side tables) (spinlock_t(自旋锁),RefcountMap(引用计算表),weak_table_t(弱引用表))
快速分流
Side Tables的本质是一张Hash表. [对象指针(Key) ==(hash函数)=>Side Table(Value)]- Hash查找: 给定值是对象的内存地址, 目标值是数组下标索引.
3. 数据结构
Spinlock_t
- “忙等”的锁
- 适用于轻量访问
RefcountMap
- hash表实现, 提高查找效率.
ptr==(DisguisedPtr(objc_object)) ==>size_t
weak_table_t
- 对象指针(key) ==(hash函数)==>
weak_entry_t(value)
4. MRC
- 手动引用计数
- (MRC特有)
retain,release,retainCount,autorelease, alloc,dealloc
5. ARC
- 自动引用计数
- ARC是
LLVM(编译器)和Runtime协作的结果. - ARC中禁用手动调用
retain,release,retainCount,dealloc(不能显式调用[super dealloc]) - ARC中新增weak, strong属性关键字
6. 引用计数管理
alloc实现
- 经过一系列的函数调用, 最终调用C函数calloc
- 此时并没有设置引用计数为1
retain实现
1 | // 两次hash查找 |
release实现
1 | SideTable & table = SideTables()[this]; |
retainCount实现
1 | SideTable & table = SideTables()[this]; |
dealloc实现 (重点)
是否可以释放的判断条件
- nonpointer_isa
- weakly_referenced
- has_assoc
- has_cxx_dtor
- has_sidetablt_rc
条件都不满足直接调用C函数free()
否则调用objc_dispose()
objc_dispose内部实现
objc_destructInstance()- C函数
free()
objc_destructInstance()内部实现
hasCxxDtor->object_cxxDestructhasAssociatedObjects->_object_remove_asscations()clearDeallocating()
clearDeallocating()内部实现
1 | ... |
7. 弱引用管理
添加weak变量
object_initWeak() -> storeWeak() -> weak_register_no_lock()
清除weak变量, 同时设置指向为nil
dealloc() -> … -> weak_clear_no_lock()
8. 自动释放池
@autoreleasepool
1 | // @autoreleasepool{} 是下面两个方法的封装回调 |
数据结构
- 是以
栈为节点通过双向链表的形式组合而成 - 是和线程一一对应的
疑点
1. viewdidload中创建的对象什么时候释放?
1 | 在当次runloop将要结束的时候, 调用AutoreleasePoolPage::pop()操作 |
2. @autoreleasepool 为什么可以嵌套调用?
1 | 多层嵌套就是多次插入哨兵对象 |
3. @autoreleasepool使用场景
1 | 在for循环中alloc图片数据等内存消耗较大的场景手动插入autoreleasepool |
9. 循环引用
分3中类型
- 自循环引用 (对象强持有(strong)成员变量, 成员变量再被赋值当前对象)
- 相互循环引用
- 多循环引用
常见场景
- 代理
BlockNSTimer- 大环引用
如何解除
- 避免产生循环引用, (
__weak,__block,__unsafe_unretained(因无引用计数, 效果同__weak)) - 断环
关于__block
MRC下,__block修饰对象不会增加引用计数, 可以避免循环引用ARC下,__block修饰对象会被强引用, 无法避免循环引用, 需要手动解环
关于__unsafe_unretained
- 修饰对象不会增加引用计数, 可以避免循环引用
- 如果被修饰对象在某一时机被释放, 会产生
悬垂指针
NSTimer循环引用问题
总结
什么是ARC?
1 | ARC是由LLVM编译器和Runtime共同协作实现自动引用计数的结果 |
为什么weak指针指向的对象在废弃之后会被自动置为nil?
1 | 当一个对象被dealloc()之后, 在dealloc的内部实现中会调用弱引用清除函数 weak_clear_no_lock(), |
唐巧
ibireme
李明杰
文顶顶
崔庆才