内存管理
引用计数
问:讲讲你对内存管理的认识?
答:新建的一个对象,引用计数=1.每被新的一个指针指向再加一,当某个指针不指向的时候就减一,直到变为0的时候,这个对象就会被回收.引用计数的好处是在多个对象之间传递和共享数据的时候,如果按照"谁申请谁释放"的原则的话,会很混乱性能也低,比如对象A生成了一个对象M,需要调用对象B的某一个方法,将对象M作为参数传递过去那么对象A就需要在对象B不再需要对象M的时候,将对象M销毁。但对象B可能只是临时用一下对象 M,也可能觉得对象M很重要,将它设置成自己的一个成员变量,那这种情况下,什么时候销毁对象M就成了一个难题。所以引用计数很好的解决了这种问题,在参数M的传递过程中,哪些对象需要长时间使用这个对象,就把它的引用计数加1,使用完了之后再把引用计数减1。所有对象都遵守这个规则的话,对象的生命期管理就可以完全交给引用计数了。我们也可以很方便地享受到共享对象带来的好处.
ARC 下的内存管理问题
ARC 能够解决 iOS 开发中 90% 的内存管理问题,但是另外还有 10% 内存管理,是需要开发者自己处理的,这主要就是与底层 Core Foundation 对象交互的那部分,底层的 Core Foundation 对象由于不在 ARC 的管理下,所以需要自己维护这些对象的引用计数。 那么我们一般会遇到哪些问题呢?
循环引用
什么是循环引用?如下图所示(图是copy的,嘻嘻),对象A和对象B互相持有对象,,而因为对象 A 的销毁依赖于对象 B 销毁,而对象 B 的销毁与依赖于对象 A 的销毁,这样就是死循环了,俩都释放不了,that which means Reference Cycle.
如何打破循环引用
主要有两个办法,第一个办法是我明确知道这里会存在循环引用,在合理的位置主动断开环中的一个引用,使得对象得以回收。常见于block的使用场景,它依赖于程序员自己有能力发现循环引用并且知道在什么时机断开循环引用回收内存(这通常与具体的业务逻辑相关)
第二个是使用弱引用
常见于delegate,两个 ViewController A 和 B,ViewController A 需要弹出 ViewController B,让用户输入一些内容,当用户输入完成后,ViewController B 需要将内容返回给 ViewController A。这个时候,View Controller 的 delegate 成员变量通常是一个弱引用,以避免两个 ViewController 相互引用对方造成循环引用问题,如下所示(图是copy的,嘻嘻)
弱引用的实现原理
系统对于每一个有弱引用的对象,都维护一个表来记录它所有的弱引用的指针地址。这样,当一个对象的引用计数为0时,系统就通过这张表,找到所有的弱引用指针,继而把它们都置成 nil。
Core Foundation 对象的内存管理
下面我们就来简单介绍一下对底层 Core Foundation 对象的内存管理。底层的 Core Foundation 对象,在创建时大多以 XxxCreateWithXxx 这样的方式创建,例如: 下面我们就来简单介绍一下对底层 Core Foundation 对象的内存管理。底层的 Core Foundation 对象,在创建时大多以 XxxCreateWithXxx 这样的方式创建,例如:
// 创建一个 CFStringRef 对象
CFStringRef str= CFStringCreateWithCString(kCFAllocatorDefault, “hello world", kCFStringEncodingUTF8);
// 创建一个 CTFontRef 对象
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);
对于这些对象的引用计数的修改,要相应的使用 CFRetain 和 CFRelease 方法。如下所示:
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);
// 引用计数加 1
CFRetain(fontRef);
// 引用计数减 1
CFRelease(fontRef);
对于 CFRetain 和 CFRelease 两个方法,读者可以直观地认为,这与 Objective-C 对象的 retain 和 release 方法等价。
所以对于底层 Core Foundation 对象,我们只需要延续以前手工管理引用计数的办法即可。
除此之外,还有另外一个问题需要解决。在 ARC 下,我们有时需要将一个 Core Foundation 对象转换成一个 Objective-C 对象,这个时候我们需要告诉编译器,转换过程中的引用计数需要做如何的调整。这就引入了bridge相关的关键字,以下是这些关键字的说明:
__bridge: 只做类型转换,不修改相关对象的引用计数,原来的 Core Foundation 对象在不用时,需要调用 CFRelease 方法。
__bridge_retained:类型转换后,将相关对象的引用计数加 1,原来的 Core Foundation 对象在不用时,需要调用 CFRelease 方法。
__bridge_transfer:类型转换后,将该对象的引用计数交给 ARC 管理,Core Foundation 对象在不用时,不再需要调用 CFRelease 方法。
我们根据具体的业务逻辑,合理使用上面的 3 种转换关键字,就可以解决 Core Foundation 对象与 Objective-C 对象相对转换的问题了.
iOS的内存管理方式之 Tagged Pointer(小对象的内存管理)
Tagged Pointer
一些七七八八
- 堆:OC对象,堆内存需要程序员去管理
- 栈:基本数据类型,超过作用域就会由系统检测回收
- 内存溢出:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
- 内存泄露:是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光