Swizzling 方法,隐式 return ARC 下的保留对象
Swizzling methods, that implicitly return a retained object under ARC
例如,让我们考虑在 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:
方法隐式returns一个保留对象,因为它属于copy
方法族。但是我的myCopyWithZone:
不是。
我预计会崩溃,但看起来这段代码可以正常工作。当然,我可以重命名我的方法以避免混淆。但我很好奇幕后究竟发生了什么?
如您所知,ARC 检查方法名称,应用 Cocoa 内存管理命名约定,并确定方法的行为方式。对于它正在编译的方法,它使该方法遵守这些约定。对于它正在调用的方法,它假定该方法遵守这些约定。
(可以使用函数属性覆盖约定,但暂时忽略它。)
当 ARC 编译您的 -myCopyWithZone:
时,它确定这样的方法应该 return +0 引用。当它遇到对(显然)-myCopyWithZone:
的调用时,它假定方法 return 是一个 +0 引用。由于那些匹配,它不应该保留或释放任何东西。 (好吧,它可能会暂时保留结果,但它必须通过自动释放来平衡它。)因此,actual +1 引用 return 由原始 -copyWithZone:
留给调用者并且调用者期望 +1 引用,所以这一切都很好。
您可能会通过调用 return 是 +1 引用的不同方法(不会通过 swizzling 有效重命名)来导致 ARC 搞砸。如果是 return 并且由于当前方法预期 return 一个 +0 引用,它会自动释放它。调用者不会保留它,因为它期望 +1 引用。因此该对象将被过早释放,可能导致崩溃。
例如,让我们考虑在 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:
方法隐式returns一个保留对象,因为它属于copy
方法族。但是我的myCopyWithZone:
不是。
我预计会崩溃,但看起来这段代码可以正常工作。当然,我可以重命名我的方法以避免混淆。但我很好奇幕后究竟发生了什么?
如您所知,ARC 检查方法名称,应用 Cocoa 内存管理命名约定,并确定方法的行为方式。对于它正在编译的方法,它使该方法遵守这些约定。对于它正在调用的方法,它假定该方法遵守这些约定。
(可以使用函数属性覆盖约定,但暂时忽略它。)
当 ARC 编译您的 -myCopyWithZone:
时,它确定这样的方法应该 return +0 引用。当它遇到对(显然)-myCopyWithZone:
的调用时,它假定方法 return 是一个 +0 引用。由于那些匹配,它不应该保留或释放任何东西。 (好吧,它可能会暂时保留结果,但它必须通过自动释放来平衡它。)因此,actual +1 引用 return 由原始 -copyWithZone:
留给调用者并且调用者期望 +1 引用,所以这一切都很好。
您可能会通过调用 return 是 +1 引用的不同方法(不会通过 swizzling 有效重命名)来导致 ARC 搞砸。如果是 return 并且由于当前方法预期 return 一个 +0 引用,它会自动释放它。调用者不会保留它,因为它期望 +1 引用。因此该对象将被过早释放,可能导致崩溃。