2016-02-18 5 views
5

Например, давайте рассмотрим следующий код под АРК:методы Swizzling, которые неявно возвращают удерживаемой объект под АРК

#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

Как вы знаете, ARC проверяет имя метода, применяет соглашения об именах управления памятью какао и определяет, как должен себя вести метод. Для метода, который он компилирует, он заставляет метод подчиняться этим соглашениям. Для метода, который он вызывает, он предполагает, что метод подчиняется этим соглашениям.

(Можно переопределить соглашения с помощью функции атрибутов, но игнорировать, что на данный момент.)

Когда ДУГА компиляции -myCopyWithZone:, он определяет, что такой метод должен возвращать +0 ссылки. Когда он встречает вызов (по-видимому) -myCopyWithZone:, он предполагает, что метод возвращает ссылку +0. Поскольку они совпадают, он не должен ни сохранять, ни выпускать что-либо. (Ну, это может временно сохранить результат, но он должен сбалансировать это с авторекламой.) В результате возвращается +1 ссылка, возвращаемая оригиналом -copyWithZone:, и вызывающий абонент ожидал ссылку +1, так что все хорошо.

Возможно, вы, возможно, вызовите ARC, вызвав другой метод (который не будет эффективно переименован swizzling), который возвращает ссылку +1. Если он должен был вернуть это, и, поскольку ожидается, что текущий метод вернет ссылку +0, он автоматически остановит его. Вызывающий не сохранил его, потому что ожидал +1 ссылку. Таким образом, объект будет преждевременно освобожден, что может привести к сбою.