5

例如,让我们考虑 ARC 下的以下代码:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@implementation NSDate (MyEvilHack)

+ (void)load {
    Method originalMethod = class_getInstanceMethod(self, @selector(copyWithZone:));
    Method newMethod = class_getInstanceMethod(self, @selector(myCopyWithZone:));
    method_exchangeImplementations(originalMethod, newMethod);
}

- (id)myCopyWithZone:(NSZone *)zone {
    id result = [self myCopyWithZone:zone];
    // do customization
    return result;
}

@end

在这段代码中,原始copyWithZone:方法隐式返回一个保留对象,因为它属于copy方法族。但我myCopyWithZone:的不是。

我预计会崩溃,但看起来这段代码工作正常。当然,我可以重命名我的方法以避免混淆。但我很好奇引擎盖下到底发生了什么?

4

1 回答 1

4

如您所知,ARC 检查方法名称,应用 Cocoa 内存管理命名约定,并确定方法的行为方式。对于它正在编译的方法,它使该方法遵守这些约定。对于它正在调用的方法,它假定该方法遵循这些约定。

(可以使用函数属性覆盖约定,但暂时忽略它。)

当 ARC 编译你的-myCopyWithZone:时,它确定这样的方法应该返回一个 +0 引用。当它遇到对 (apparently) 的调用时-myCopyWithZone:,它假定该方法返回一个 +0 引用。由于这些匹配,它不应该保留或释放任何东西。(好吧,它可能会暂时保留结果,但它必须通过自动释放来平衡它。)结果,原始返回的实际+1 引用会保留-copyWithZone:给调用者,而调用者期待 +1 引用,仅此而已好的。

您可能会通过调用返回 +1 引用的不同方法(不会通过 swizzling 有效地重命名)来导致 ARC 搞砸。如果要返回它,并且由于当前方法预计返回 +0 引用,它将自动释放它。调用者不会保留它,因为它期望 +1 引用。所以对象会被过早地释放,很可能导致崩溃。

于 2016-02-19T00:32:38.510 回答