Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swizzling methods, that implicitly return a retained object under ARC

For example, lets consider following code under 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

In this code, original copyWithZone: method is implicitly returns a retained object, because it belongs to copy method family. But my myCopyWithZone: is not.

I expect crash, but looks like this code works normally. Of course, I can rename my method to avoid confusion. But I am curious what exactly happens under the hood?

like image 522
Borys Verebskyi Avatar asked Sep 25 '22 08:09

Borys Verebskyi


1 Answers

As you know, ARC examines the method name, applies the Cocoa memory management naming conventions, and determines how a method should behave. For a method that it's compiling, it makes the method obey those conventions. For a method that it's calling, it assumes the method obeys those conventions.

(One can override the conventions using function attributes, but ignore that for now.)

When ARC is compiling your -myCopyWithZone:, it determines that such a method should return a +0 reference. When it encounters the call to (apparently) -myCopyWithZone:, it assumes that method returns a +0 reference. Since those match, it should neither retain or release anything. (Well, it may temporarily retain the result, but it must balance that with an autorelease.) As a result, the actual +1 reference returned by the original -copyWithZone: survives to the caller and the caller was expecting a +1 reference, so that's all good.

You could probably cause ARC to screw up by calling a different method (which would not be effectively renamed by swizzling) that returns a +1 reference. If it were to return that and since the current method is expected to return a +0 reference, it would autorelease it. The caller would not retain it because it was expecting a +1 reference. So the object would be prematurely deallocated, likely leading to a crash.

like image 174
Ken Thomases Avatar answered Oct 12 '22 10:10

Ken Thomases