这是 看 书籍 Objective-C 高级编程 ios与os x多线程和内存管理 一书的随记。
1 arc
首先记录一下几个调试的东西
调试
1.查看 autoreleasePool 池里面的对象
extern void _objc_autoreleasePoolPrint();
_objc_autoreleasePoolPrint();
2.通过 clang 可以 查看对应的 汇编代码。其实xcode 的 Product-》Perform Action ->Assemble "" 也就ok了
clang -fobjc-arc -framework Foundation test.m -o test
3 通过 CFGetRetainCount((__bridge CFTypeRef)test2)); 或者
extern uintptr_t _objc_rootRetainCount();
NSLog(@"xxxx %lu",_objc_rootRetainCount(self.array));
可以在 arc 总看到 retaincount 的值。切记 不要依赖这个retaincount 编译器 有很多优化。可能跟你理解的不同。比如 当你 通过一个非 alloc new copy mutableCopy 方法获取一个对象的时候 按照道理是 获取的 autorelease 对象。然后在 retain 。编译器 会优化省略 autorelease 过程。
引用计数的思考方式
自己生成的对象,自己所持有
非自己生成的对象,自己也能持有
不在需要自己持有对象时释放
非自己持有的对象 无法释放
方法名称 以 alloc new copy mutableCopy 开头的方法 必须返回给调用者应当持有的对象。
以init 开头的 更加严格 必须返回 id 类型 或者当前类的 类型 或者 子类父类。这个对象不会注册到 autorelease 池里面去。只是
修饰符
_strong
id 类型 和对象类型 默认的修饰符。即:
id xx = [[NSObject alloc] init]; 实际上就等于
id _strong obj = [[NSObject alloc] init ];
简单的说 就是当 strong 修饰符修饰的变量 被赋值的时候。 就相等于 retain 了该对象。 当该修饰符 修饰的变量 抛弃原本指向的对象的时候。 就相当于 release了 该对象。当strong 修饰的变量结束了有效区域的时候。也会release
- MyTest *test = [[MyTest alloc]init];
- MyTest *test2 = test;
- MyTest *test3 = test;
- MyTest *test4 = test;
- MyTest *test5 = test;
- NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge
- CFTypeRef)test));
- test5 = nil;
- NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge
- CFTypeRef)test));
- test4 = [[MyTest alloc]init];
- NSLog(@"Retain count is %ld", CFGetRetainCount((__bridge
- CFTypeRef)test));
如上代码所示。因为默认修饰符是strong 所以 当test 赋值给strong修饰的变量的时候。 就retain了 test。所以第一个输出是5. 当 执行 test5 = nil 的时候。 因为 被修饰符为 strong 的变量 抛弃的对象会release 所以 变成了4. 同样道理 最后 test retainCount 变成了3;(ps:这里要说的是 之前在有的资料看过 非arc 跟 arc 混编的 复杂情况下 有可能导致的内存泄露。 主要是 对象是非arc 创建的 在 arc中使用的情况。 当时 给出的解决方案。就是 用完之后 设置为 nil 就行了。 想想这里的了解。其实有一定道理。当然现在找不到那个资料了所以 也没法实验总结)
_weak
当2个对象互相引用导致引用环的时候就需要weak了。 简单的说就是赋值的时候 不改变 retainCount的概念。当然还有当对象释放之后 会设置为 nil。这也是 _weak 和 __unsafe_unretain 的主要区别。
这里有个需要注意的。当使用__weak 变量的时候。不是简单的访问。 是会调用一个方法 retainWeakReference (可以重写这个非公开方法 用来调试。但正式开发千万别加逻辑。) 书上说 会加入到 autoreleasePool 里面去。可是访问结束查看 autoreleasePool 。在使用访问 __weak的时候 也确实可以查看到 retainCount 增加了1. 这里可以 猜测 是只是访问的那里新建了一个 autoreleasePool池。访问结束 就 已经释放了。
这样的作用是因为 weak 不持有该对象。当访问过程中。可能被释放掉。 这样就能保证访问过程中都是有效状态。
_unsafe_unretained
我能想到的跟 __weak 的区别就是 释放了对象的时候 不设置为 nil。用这个修饰符的对象 不被arc内存管理 所管理。
_autoreleaseing
autorelease 的本质就是 创建一个NsAutoreleasePool 对象。当一个对象调用 autorelease 的时候。 会去获取当前使用的 最内侧(如果有嵌套的池的话)的NsAutoreleasePool对象。然后调用 NsAutoreleasePool对象的一个 类似数组 的addobject 方法。让 NsAutoreleasePool对象来持有这个 autorelease 对象。 当调用 NsAutoreleasePool 的 drain方法的时候。清除引用。 就释放了 autorelease对象。
实际上在arc 中 。编译器有很多优化。让原本是autorelease 的对象 根本就不加入到 NSAutoreleasePool中去。
pd:这里需要注意的是 指针变量 默认的修饰符 是 autoreleaseing 比如 id *xx。 是 autoreleaseing的 而 id xx 是 strong的。
属性
属性 | 修饰符 |
assign | __unsafe_unretained |
copy | __strong(是retain的复制的新对象) |
retain | __strong |
strong | __strong |
unsafe_unretained | __unsafe_unretained |
weak | __weak |
另外这里测试的时候 发现 如果在方法里面 有个 __weak属性 每次使用 属性都会导致retainCount +1. 查看汇编代码 有个
_objc_retainAutoreleasedReturnValue 方法被调用了。完全不知道为什么。。
看了下 _objc_autoreleasedReturnValue 函数 返回 注册到 autoreleasepool 里面的对象。 但是 跟 autorelease 函数又不同。 如果 _objc_autoreleasedReturnValue 方法之后 会立即调用 _objc_retainAutoreleasedReturnValue 就不将对象注册到 autoreleasepool 中。而是直接传递到方法或者函数的调用方。 _objc_retainAutoreleasedReturnValue 和 objc_retain 函数不同。 这个作用 就是 上面说到的。 对于不是 new alloc copy mutablecopy 开头的方法。按道理返回的对象是 autorelease 然后在retain 。这个机制就优化了这里的代码