书名:《Object-C高级编程 IOS与OS X多线程和内存管理》
ARC
在Object-C中采用Automatic Reference Counting(ARC)机制,让编译器来进行内存管理。
- 使用Xcode4.2或以上版本
- 使用LLVM编译器3.0或以上版本
- 编译器选项中设置ARC为有效
所有权修饰符
- __strong修饰符
- __weak修饰符
- __unsafe_unretained修饰符
- __autoreleasing修饰符
1. __strong修饰符
__strong修饰符为id类型和对象类型默认的所有权修饰符,可以省略。
id __strong obj = [[NSObject alloc] init];
2. __weak修饰符
如果只用__strong修饰符,自动引用计数式内容管理必然会发生“循环引用”的问题。
__weak提供弱引用,弱引用不能持有对象实例,可以解决“循环引用”的问题。
下面这段代码:
id __weak obj = [[NSObject alloc] init];
NSLog(@"%@",obj);
会输出 nil 。因为__weak修饰的obj持用弱引用,在赋值过后,并没有对象强引用它,生成的对象会立即释放。这样obj自动赋值为nil。
再来看看下面这段代码的输出:
id __weak obj0 = nil;
{
id __strong obj1 = [[NSObject alloc] init];
obj0 = obj1;
NSLog(@"%@",obj0);
}
NSLog(@"%@",obj0);
结果:
<NSObject: 0x109420000>
nil
注:weak修饰符只能用于IOS5以上以及OS X Lion以上版本的应用程序。在它们一下的程序可使用unsafe_unretained修饰符来代替。
3. __unsafe_unretained修饰符
unsafe_unretained修饰符,是不安全的所有权修饰符。有unsafe_unretained修饰符的变量不属于编译器的内存管理对象。
id __unsafe_unretained obj = [[NSObject alloc] init];
NSLog(@"%@",obj);
这里和__weak一样,会输出nil
。难道这两个修饰符相同吗?再来看第二个例子:
id __unsafe_unretained obj0 = nil;
{
id __strong obj1 = [[NSObject alloc] init];
obj0 = obj1;
NSLog(@"%@",obj0);
}
NSLog(@"%@",obj0);
输出:
<NSObject: 0x10972ced0>
<NSObject: 0x10972ced0>
可以看到结果已经和__weak不同。
这里在第二次打印的时候,obj0已经成为一个野指针。
weak与unsafe_unretained的区别可以简单地说:
当弱引用的对象被释放,weak修饰的对象会自动赋值为nil;而unsafe_unretained修饰的对象不会。
4. __autoreleasing修饰符
在ARC有效的时候,不能使用NSAutoreleasePool。为了实现想非ARC工程的NSAutoreleasePool该怎么办?使用__autoreleasing修饰符。
一下两段代码效果相同:
/* NO ARC */
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
/* ARC */
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}
但是,显式地附加autoreleasing修饰符同显式地附加strong一样罕见。一般不必显示地添加__autoreleasing修饰符。
另外,无论ARC是否有效,调试用的私有方法_objc_autoreleasePoolPrint()都可以使用。利用这个函数可以有效地帮助我们调试注册到autoreleasepool上的对象。
ARC的规则
1. 不能使用retain/release/retainCount/autorelease
2. 不能使用NSAllocateObject/NSDeallocateObject
3. 遵守内存管理的方法命名规则
4. 不要显示调用dealloc
5. 使用@autoreleasepool快代替NSAutoreleasePool
6. 不能使用NSZone
7. 对象型变量不能作为C语言结构体的成员
要把对象型变量加入到结构体成员,可强制转换为void* 或是添加__unsafe_unretained
修饰符。
显示转换id 和 void*
下面这段代码,在ARC下会编译错误:
id obj = [[NSObject alloc] init];
void* p = obj;
需要使用“__bridge转换”:
1、 __bridge
/* ARC */
id obj1 = [[NSObject alloc] init];
void* p = (__bridge void*)obj1;
相当于:
/* NO ARC */
id obj = [[NSObject alloc] init];
void* p = obj;
但是使用bridge转换,其安全性与赋值unsafe_unretained修饰符相似,甚至更低。
2、 __bridge_retained
/* ARC */
id obj1 = [[NSObject alloc] init];
void* p = (__bridge_retained void*)obj1;
相当于:
/* NO ARC */
id obj1 = [[NSObject alloc] init];
void* p = obj1;
[(id)p retain];
下面的代码:
void* p = 0;
{
id obj1 = [[NSObject alloc] init];
void* p = (__bridge_retained void*)obj1;
}
NSLog(@"%@",p);
由于使用__bridge_retained,p在对后持有该对象,所以会打印出该对象。
3、 __bridge_transfer
/* ARC */
id obj1 = [[NSObject alloc] init];
void* p = (__bridge_transfer void*)obj1;
相当于:
id obj1 = [[NSObject alloc] init];
void* p = obj1;
[(id)p retain];
[obj1 release];
当然可是实现双向的转换。这些转换多数使用在Objec-C对象和Core Foundation对象之间的互相转换。
以下两个函数为系统提供的方法,实现Toll-Free Bridging:
NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) {
return (__bridge_transfer id)X;
}
NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetain(id X) {
return (__bridge_retained CFTypeRef)X;
}
Toll-Free Bridging
属性
ARC中,属性声明的属性与所有权修饰符的关系
属性声明 |
所有权修饰符 |
assign |
__unsafe_unretained |
copy |
__strong(复制对象) |
retain |
__strong |
strong |
__strong |
unsafe_unretained |
__unsafe_unretained |
weak |
__weak |
有一种情况不能使用__weak修饰符:
- (BOOL)allowsWeakReference UNAVAILABLE_ATTRIBUTE;
- (BOOL)retainWeakReference UNAVAILABLE_ATTRIBUTE;
如果NSobject实例上面的两个方法返回NO,绝对不能使用__weak修饰符。