2016-02-18 15 views
5

Na przykład, rozważmy następujący kod poniżej łuku:metody Swizzling, że niejawnie zwrócić zatrzymane obiekt pod 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 

W tym kodzie, oryginalne copyWithZone: metoda jest domyślnie zwraca zatrzymany obiektu, ponieważ należy do copy rodzina metod. Ale mój myCopyWithZone: nie jest.

Spodziewam się awarii, ale wygląda na to, że ten kod działa normalnie. Oczywiście mogę zmienić nazwę mojej metody, aby uniknąć nieporozumień. Ale ciekawi mnie, co dokładnie dzieje się pod maską?

Odpowiedz

4

Jak wiesz, ARC bada nazwę metody, stosuje konwencje nazewnictwa zarządzania pamięcią masową Cocoa i określa, jak powinna zachowywać się metoda. W przypadku metody, którą kompiluje, sprawia, że ​​metoda jest zgodna z tymi konwencjami. Dla wywoływanej metody zakłada, że ​​metoda jest zgodna z tymi konwencjami.

(Można zastąpić konwencje użyciem funkcji atrybutów, ale ignorować faktu, że na razie).

Kiedy ARC kompilacji -myCopyWithZone:, stwierdzi, że taka metoda powinna zwracać +0 odniesienia. Kiedy napotka wywołanie (pozornie) -myCopyWithZone:, zakłada, że ​​ta metoda zwraca referencję +0. Od tego meczu nie powinien niczego zatrzymywać ani wydawać. (Cóż, może tymczasowo zachować wynik, ale musi to zrównoważyć z autoreassem.) W rezultacie referencja aktualna +1 zwrócona przez oryginalną -copyWithZone: przetrwa dzwoniącemu, a dzwoniący oczekiwał odniesienia +1. więc to wszystko jest dobre.

Prawdopodobnie możesz spowodować, że ARC zepsułoby się, wywołując inną metodę (która nie zostałaby skutecznie przemianowana przez swizzling), która zwróci referencję +1. Jeśli miałaby to zwrócić, a ponieważ obecna metoda ma zwrócić wartość +0, autorelease go. Dzwoniący nie zachowałby go, ponieważ oczekiwał odniesienia +1. Tak więc obiekt zostałby przedterminowo zwolniony, co prawdopodobnie doprowadziłoby do wypadku.

Powiązane problemy