Side effects of ARC ARC 的辐射影响

近年来, ARC 计数被广泛使用,在大多数的项目里面都会用到它。所以你可能觉得不用了解那些犄角旮旯的东西,但是恐怕没有那么简单哦。由于在 Objective-C 里,ARC 是非常基础的底层机制,它很容易就会发生编译错误。有时候哪怕你觉得自己用得挺好的,代码也跑不起来。

下面还是用例子说话。假设一下你创建了一个类来帮助人们搬家,那这个类会有两个属性:旧地址和新地址,我们可以这样去声明:

@property NSString *oldAddress;
@property NSString *newAddress;

但是上面的代码是不能被编译的。

还记得属性有两个访问器吗?所以这两个属性就有四种方法:oldAddress, setOldAddress, newAddress, 和 setNewAddress。由于苹果非常严格的命名规范,当 ARC 看到这四种方法的时候,它看到了 newAddress 就认为这里要创建一个强引用对象,但实际上它不是这样的。这就和 ARC 的规则相悖,继而产生编译错误。

解决它的方法是自定义 getter 访问器的名字,让它遵守命名规则:

@property (getter=getNewAddress) NSString *newAddress;

你可以学着像 ARC 那样来思考,这个听起来很奇怪还有点儿傻,但是确实只有这样,才能写好代码。编译器必须区分哪里调用了 retain, release, 或者 autorelease,否则就会造成内存问题。

还有一个很好的例子,用到了 NSError,我们来看下面的代码:

NSError *error;
NSString *str = [NSString stringWithContentsOfFile:@"somefile.txt"
usedEncoding:nil error:&error];

这段代码很常见,而且我经常会鼓励初学者这样写代码,因为可读性非常强。但是一旦你稍微精进了一些之后,就会发现这里有一个问题。ARC 在这 error 对象上的用了默认的 strong,但是在 stringWithContentsOfFile 方法里,它想要自动释放 error,非常细小的错误,但是依然是个错误。

ARC 还是很机智的,它不会强行让你自己解决这个问题。我们来看 ARC 怎么自己搞定的。

首先呢,它会主动把 error 设为 strong,执行过程就像这样:

NSError * __strong error;

然后它会创建一个新的临时的 NSError 对象,这个对象是可以被自动释放的,然后把刚才 error 的值,拷贝给它:

NSError * __autoreleasing temp = error;

这个临时存在的对象(temp)就有了值,并且可以在 stringWithContentsOfFile 里被调用:

NSString *str = [NSString stringWithContentsOfFile:@"somefile.txt"
usedEncoding:nil error:&temp];

最后 ARC 会 temp 返回给 error,一切照常:

error = temp;

如果你不想 ARC 来做这个额外工作的话,你可以这样用 __autoreleasing 来声明 error:

NSError * __autoreleasing error;

除此之外,ARC 还会影响到一些第三方库,如果这个第三方库不及时更新的话,可能会造成误伤。好在近几年这种情况越来越少啦。

results matching ""

    No results matching ""