2013-07-03 51 views
0

IOS 6.1IOS RemoveObserver的鍵值對異常會導致額外的保留計數爲ARC

我們已經注意到,當我們得到removeObserver的鍵值對的例外是不存在的,具有KVP得到的類並從removeObserver調用中額外保留計數。

以下是一些證明這一點的測試代碼。還有一個橋接版本解決了這個問題。

任何意見,歡迎....

#import "ViewController.h" 
#import "ClassA.h" 

@interface ViewController() 

@property (strong, nonatomic) ClassA* classA; 

@end 

@implementation ViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad];  
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    if ([keyPath isEqualToString:@"radarOn"]) 
    { 
     NSLog(@"--- here in radaron"); 
     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"Here" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 
     [alert show]; 
    } 
} 

- (IBAction)CreateClassAAction:(id)sender 
{ 
    self.classA = [[ClassA alloc] init]; 
} 

- (IBAction)SendNotificationAction:(id)sender 
{ 
    self.classA.radarOn = ! self.classA.radarOn; 
} 

- (IBAction)ClearKVPAction:(id)sender 
{ 
    @try 
    { 
     [self.classA removeObserver:self forKeyPath:@"radarOn"]; 
    } 
    @catch (NSException *exception) 
    { 
     NSString *s = [NSString stringWithFormat:@"Exception ClassA Retain Count %ld %@", CFGetRetainCount((__bridge CFTypeRef)(self.classA)), exception.description]; 
     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:s delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 
     [alert show]; 
     // this will let the class release 
     // CFBridgingRelease((__bridge CFTypeRef)(self.classA)); 
    } 
} 

- (IBAction)AddKVPAction:(id)sender 
{ 
    [self.classA addObserver:self forKeyPath:@"radarOn" options:NSKeyValueObservingOptionNew context:nil]; 
} 

@end 

#import <Foundation/Foundation.h> 

@interface ClassA : NSObject 

@property (nonatomic, assign) BOOL radarOn; 

@end 

#import "ClassA.h" 

@implementation ClassA 

- (void) dealloc 
{ 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"ClassA Dealloc" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 
    [alert show]; 
} 

@end 
+0

'ClearNotificationsAction'應該做什麼?你不應該從'UIViewController'在默認通知中心調用'removeObserver:';你不能保證你的超類可能已經註冊了哪些通知。由於這似乎是ARC啓用,我不確定你可以可靠地使用'reatainCount'。您是否使用過儀器來確認它正在泄漏? – ppilone

+0

這只是另一個測試,可以忽略這個討論。將刪除。當然還有文書,否則不會發布。該版本旨在允許該類由於保留計數錯誤而被釋放。 – ort11

+0

問題解決了。這是一個例外/ ARC問題,而不僅僅是一個removeObserver問題。當我可以但將詳細答案,而不是self.classA removeObserver使用_classA removeObserver – ort11

回答

0

這樣的代碼似乎工作......(沒有額外的保留)

- (IBAction)ClearKVPAction:(id)sender 
{ 
    @try 
    { 
     [_classA removeObserver:self forKeyPath:@"radarOn"]; 
    } 
    @catch (NSException *exception) 
    { 
    } 
} 

- (IBAction)ClearKVPAction:(id)sender 
{ 
    ClassA* temp = self.classA; 
    @try 
    { 
     [temp removeObserver:self forKeyPath:@"radarOn"]; 
    } 
    @catch (NSException *exception) 
    { 
    } 
} 

添加-fobjc弧例外問題.m文件(S)。

0

這看起來可能是ARC lifted from the Clang 3.4 documentation的 「功能」:

例外

默認情況下,在Objective C,ARC是不是異常安全的正常 版本:

當__strong變量的作用域 異常終止異常時,它不會終止__strong變量的生命週期。它不執行 版本,如果 完整表達式拋出異常,則該版本將發生在完整表達式的末尾。程序可以編譯爲 選項-fobjc-arc-exceptions以啓用這些選項,或者使用 選項-fno-objc-arc-exceptions來明確禁用它們,最後一個參數爲「獲勝」。

原理標準Cocoa約定是,例外信號 程序員錯誤,並不打算從中恢復。在默認情況下,使代碼異常安全,會對代碼造成嚴重的運行時間和代碼損失,通常實際上並不關心 例外安全性。因此,ARC生成的代碼在默認情況下會在 例外情況下泄漏,如果該進程將立即終止,那麼這很好。那些關心從異常中恢復 的程序應該啓用該選項。在Objective-C++中, -fobjc-arc-exceptions默認是啓用的。

理由C++已經引入了ARC介紹的普遍異常 - 清理代碼 。還沒有 已禁用的異常的C++程序員更有可能要求異常安全。除非 編譯器中的例外被禁用,否則當 異常終止其範圍時,ARC會終止__weak對象的生命週期。我們希望 在這裏更安全。當然,如果程序 確實嘗試從異常中恢復,那麼潛在的大規模泄漏大概可能會導致進程崩潰,因爲此損壞是。

+0

是的,最終發現這一點。這實際上是一個奇怪的默認設置,因爲異常是一種非常常見的/常規的實現方式。我認爲這是不正確的,默認情況下啓用了-fobjc-arc-exceptions,至少不是在應用程序或發佈的測試應用程序中。一旦標誌被添加,啓動問題/泄漏是「固定的」。 – ort11