拜伦的博客

Coder Byron

这里有关于iOS,机器学习的笔记心得,欢迎交流


iOS底层(7)- 内存管理

目录

浅拷贝和深拷贝

拷贝的最终目的是产生一个副本,修改副本不会对原对象产生影响。故到底是深拷贝还是浅拷贝要根据这个规则来判断。
  • [任意对象 copy] 产生一个不可变对象,如果原对象是不可变对象,那么就是浅拷贝,因为不可变对象本身就不能修改,所以没必要创新分配内存。原对象是可变对象,就是深拷贝。
  • [任意对象 mutableCopy] 产生一个可变对象,不论原对象是否是可变对象,都是深拷贝,因为要产生修改不影响原对象的副本,必须重新分配内存。

定义属性时使用copy修饰词,目的是在赋值之后产生一个不可变对象,那么在修改外部变量的时候不影响内部,例如UI控件的text都是copy修饰,防止外部修改影响内部实现。

会对target 强引用,如果target也强引用他们,就发生循环引用问题 解决方法是使用NSProxy,是和NSObject同一级别的基类,但是没有实现和声明多余的方法,只有alloc等。 作用是只要方法列表里找不到方法,跳过动态方法解析,直接做转发。且NXProxy中弱引用target即可。

内存布局

2

Tagged Pointer

从64位开始引入,目的是优化NSNumber, NSDate, NSString等小对象的存储,提高内存使用率
原来NSNumber占用一个对象的空间,指针还指向堆地址的值,最终可能只是为了存一个3。 就浪费很大空间

在使用Tagged pointer技术后,NSNumber 3的地址0x327, Tag+Data 的形式存储,从地址就能读到真实值,不需要额外空间。当内存地址长度超过64位后,才使用动态分配内存的方式存储数据。

如果地址最低有效位是1, 则是Tagged pointer

注意 : 字符串想要使用tagged pointer, 初始化要用[NSString stringWithFormat:],如果是@”xx”创建的是常规对象存在常量区。

引用计数管理

新建OC对象引用计数默认是1,当调用alloc,new,copy,mutableCopy时对象引用计数会+1

引用计数存储3

在64位优化后的isa共用体结构中,引用计数=extra_rc + sidetable_rc.refcnts[self].count,不是每个对象都会使用sidetable_rc,要看isa的标志位has_sidetable_rc.
如果isa不是共用体结构,引用计数=sidetable_rc.refcnts[self].count 4

refcnts是一个散列表,使用对象的地址为key,value为引用计数。 获取引用计数、retain、release操作都是获取extra_rc的值+sidetable的值+1或-1. 如果是release则判断是否为0,为0就dealloc。

weak 指针原理

__weak: 弱引用对象,当对象销毁时指针置为空
__unsafe_unretain: 弱引用对象,当对象销毁时,指针不变,使用会发生坏内存访问

weak能在对象销毁置为空的原理: 当自动调用dealloc方法时,判断是否有弱引用和sidetable_rc,(弱引用和超过计数器时都使用到了sidetable),有弱引用就将sidetable中自己对应的value取出来,得到一个__weak对象的地址集合(不确定是不是数组,因为用到C++的遍历器), 并遍历将其置为nil 5

自动释放池

主要通过AutoreleasePoolPage来管理,结构如下

6

在@autoreleasePool代码开始时,调用AutoreleasePoolPagePush方法,将BOUNDARY 添加到Page中,标记为开始,后续调用autorelease的对象也会添加到该Page中,在括号结束时,调用Pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY

对象释放时机

MRC: 对象创建时调用了autorelease方法
系统在主线程的Runloop中注册了2个Observer,1个监听kCFRunLoopEntry时间,调用objc_autoreleasePoolPush()。另一个监听kCFRunLoopBeforeWaiting事件,调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()。 监听kCFRunLoopBeforeExit事件调用objc_autoreleasePoolPop()
例如:viewdidload和viewwillappear 在一个loop中,即一次执行所有的block和sources,直到执行完毕loop睡眠,在睡眠之前会释放对象。

ARC: 在括号结束时,LLVM编译器自动给对象release一次,而不是在runloop中执行。

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦