NSError * __strong e;
-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;{
....
*paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
}
//The compiler therefore rewrites the code
NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
// Report the error. }
+(MyCustomClass*) myCustomClass
{ return [[[MyCustomClass alloc] init] autorelease];}
This code guarantees the returning object is autoreleased. What's the equivalent with ARC?-->simply use:
+(MyCustomClass*) myCustomClass
{ return [[MyCustomClass alloc] init];}
之前在MRC的时候,需要autorealease是因为和init对应,遵守规则。所以进入了一个误区,认为方法return之后,方法里的局部变量都会被释放掉,包括return的类。其实方法结束后,方法里的局部变量会被释放掉,但是return的类不会。所以ARC下,方法不需要做什么修改,方法返回的类遵循着ARC规则。而MRC中添加autorelease而不是release是防止类在方法return之前将对象释放掉。
3. 因为ARC中成员/局部变量默认的指针类型就是strong;
4. ARC相比MRC对一些代码带来了影响:例如,例1没意义了,例2变正常了
NSLog(@"%@",str); //输出是"(null)" id obj = [array objectAtIndex:0];
[array removeObjectAtIndex:0];
.#ifndef PX_STRONG
.#if __has_feature(objc_arc)
.#define PX_STRONG strong
.#else
.#define PX_STRONG retain
.#endif
.#endif
.#ifndef PX_WEAK
.#if __has_feature(objc_arc_weak)
.#define PX_WEAK weak
.#elif __has_feature(objc_arc)
.#define PX_WEAK unsafe_unretained
.#else
.#define PX_WEAK assign
.#endif
.#endif
.#if __has_feature(objc_arc)
.#define PX_AUTORELEASE(expression) expression
.#define PX_RELEASE(expression) expression
.#define PX_RETAIN(expression) expression
.#else
.#define PX_AUTORELEASE(expression) [expression autorelease]
.#define PX_RELEASE(expression) [expression release]
.#define PX_RETAIN(expression) [expression retain]
.#endif
例如dealloc方法:
- (void)dealloc {
......
[self removeObserver:self forKeyPath:keyPath];//如果有的话
#if !__has_feature(objc_arc)
[array release];
[super dealloc];
#endif
}
//我们先来看一下ARC无效的时候,我们写id类型转void*类型的写法
id obj = [[NSObject alloc] init];
void *p = obj;
//反过来,当把void*对象变回id类型时,只是简单地如下来写:
id obj = p;
[obj release];
//但是上面的代码在ARC有效时,就有了下面的错误:
error: implicit conversion of an Objective-C pointer
to ’void *’ is disallowed with ARC
error: implicit conversion of a non-Objective-C pointer
type ’void *’ to ’id’ is disallowed with ARC
为了解决这一问题,我们使用 __bridge 关键字来实现id类型与void*类型的相互转换。看下面的例子。
id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
将Objective-C的对象类型用 __bridge 转换为 void* 类型和使用 __unsafe_unretained 关键字修饰的变量是一样的。被代入对象的所有者需要明确对象生命周期的管理,不要出现异常访问的问题。
除过 __bridge 以外,还有两个 __bridge 相关的类型转换关键字:__bridge_retained 和 __bridge_transfer,接下来,我们将看看这两个关键字的区别:
id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void *)obj;
// 从名字上我们应该能理解其意义:类型被转换时,其对象的所有权也将被变换后变量所持有。
//如果不是ARC代码,类似下面的实现:
id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];
//可以用一个实际的例子验证,对象所有权是否被持有。
void *p = 0;
{
id obj = [[NSObject alloc] init];
p = (__bridge_retained void *)obj;
}
NSLog(@"class=%@", [(__bridge id)p class]);
//出了大括号的范围后,p 仍然指向一个有效的实体。说明他拥有该对象的所有权,该对象没有因为出其定义范围而被销毁。
__bridge_transfer: 相反,当想把本来拥有对象所有权的变量,在类型转换后,让其释放原先所有权的时候,需要使用 __bridge_transfer 关键字。
//如果ARC无效的时候,我们可能需要写下面的代码。
// p 变量原先持有对象的所有权
id obj = (id)p;
[obj retain];
[(id)p release];
//那么ARC有效后,我们可以用下面的代码来替换:
// p 变量原先持有对象的所有权
id obj = (__bridge_transfer id)p;
可以看出来,__bridge_retained 是编译器替我们做了 retain 操作,而 __bridge_transfer 是替我们做了 release。
在iOS世界,主要有两种对象:Objective-C 对象和 Core Foundation 对象0。Core Foundation 对象主要是有C语言实现的 Core Foundation Framework 的对象,其中也有对象引用计数的概念,只是不是 Cocoa Framework::Foundation Framework 的 retain/release,而是自身的 CFRetain/CFRelease 接口。
这两种对象间可以互相转换和操作,不使用ARC的时候,单纯的用C原因的类型转换,不需要消耗CPU的资源,所以叫做 Toll-Free bridged。比如 NSArray和CFArrayRef, NSString和CFStringRef,他们虽然属于不同的 Framework,但是具有相同的对象结构,所以可以用标准C的类型转换。
//比如不使用ARC时,我们用下面的代码:
NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = (CFStringRef)string;
//同样,Core Foundation类型向Objective-C类型转换时,也是简单地用标准C的类型转换即可。
//但是在ARC有效的情况下,将出现类似下面的编译错误:
Cast of Objective-C pointer type ‘NSString *’ to C pointer type ‘CFStringRef’ (aka ‘const struct __CFString *’) requires a bridged cast
Use __bridge to convert directly (no change in ownership)
Use __bridge_retained to make an ARC object available as a +1 ‘CFStringRef’ (aka ‘const struct __CFString *’)
//错误中已经提示了我们需要怎样做:用 __bridge 或者 __bridge_retained 来转型,其差别就是变更对象的所有权。
正因为Objective-C是ARC管理的对象,而Core Foundation不是ARC管理的对象,所以才要特意这样转换,这与id类型向void*转换是一个概念。也就是说,当这两种类型(有ARC管理,没有ARC管理)在转换时,需要告诉编译器怎样处理对象的所有权。
上面的例子,使用 __bridge/__bridge_retained 后的代码如下:
// __bridge
NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = (__bridge CFStringRef)string;
//只是单纯地执行了类型转换,没有进行所有权的转移,也就是说,当string对象被释放的时候,cfString也不能被使用了。
//__bridge_retained
NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = (__bridge_retained CFStringRef)string;
...
CFRelease(cfString);
// 由于Core Foundation的对象不属于ARC的管理范畴,所以需要自己release
//使用 __bridge_retained 可以通过转换目标处(cfString)的 retain 处理,来使所有权转移。即使 string 变量被释放,cfString 还是可以使用具体的对象。只是有一点,由于Core Foundation的对象不属于ARC的管理范畴,所以需要自己release。
实际上,Core Foundation 内部,为了实现Core Foundation对象类型与Objective-C对象类型的相互转换,提供了下面的函数。
CFTypeRef CFBridgingRetain(id X) {
return (__bridge_retained CFTypeRef)X;
}
id CFBridgingRelease(CFTypeRef X) {
return (__bridge_transfer id)X;
}
//所以,可以用 CFBridgingRetain 替代 __bridge_retained 关键字:
NSString *string = [NSString stringWithFormat:...];
CFStringRef cfString = CFBridgingRetain(string);
...
CFRelease(cfString);
// 由于Core Foundation不在ARC管理范围内,所以需要主动release。
//__bridge_transfer
所有权被转移的同时,被转换变量将失去对象的所有权。当Core Foundation对象类型向Objective-C对象类型转换的时候,会经常用到
CFStringRef cfString = CFStringCreate...();
NSString *string = (__bridge_transfer NSString *)cfString;
// CFRelease(cfString);
//因为已经用 __bridge_transfer 转移了对象的所有权,所以不需要调用 release
//同样,我们可以使用 CFBridgingRelease() 来代替 __bridge_transfer 关键字。
CFStringRef cfString = CFStringCreate...();
NSString *string = CFBridgingRelease(cfString);
总结
参考: