2017-01-16 59 views
0

我有一個這樣的對象:爲什麼我的對象在沒有ARC的情況下在Xcode中切換線程時會自行釋放?

typedef void (^ Completion) (Response *); 

// Response class 
@interface Response : NSObject { 
    NSDictionary * kdata; 
} 
- (id)initWithJson:(NSDictionary *)data; 
@property (nonatomic, assign) NSDictionary * data; 
@end 

@implementation Response 
- (id)initWithJson:(NSDictionary *)data { kdata = data; } 
- (NSDictionary *) data     { return kdata; } 
- (void) setData: (NSDictionary *)data { kdata = data; } 
- (NSDictionary *) msg     { return kdata[@"msg"]; } 
@end 


// inside a networking class X implementation 
- (void) doSomething:(completionBlock)completion { 
    NSDictionary * json = // get from networking function, which will always have key "msg". 
    Response * responseObj = [[Response alloc] initWithJson:json]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     if (completion != nil) { completion (responseObj); } 
    }); 
} 


// inside caller method 
[X doSomething:^(Response * response) { 
    NSLog (@"%@", [response msg]); 
} 

此代碼將提高對訪問kdata[@"msg"]錯誤,即使我敢肯定與該物體用字典正確初始化調試包含關鍵"msg"。當我調試對象時,在觀察窗口,它顯示出kdata數據類型不斷變化,從NSArrayM,NSSet,NSDictionary等等,其內容也不斷變化。我甚至在調用completion ([responseObj retain]);時添加retain關鍵字,但仍然產生錯誤。

但是,如果在X類的代碼變成這樣:

// inside a networking class X implementation 
- (void) doSomething:(completionBlock)completion { 
    NSDictionary * json = // get from networking function, which will always have key "msg". 
    Response * responseObj = [[Response alloc] initWithJson:json]; 
    if (completion != nil) { completion (responseObj); } // here is the change, no more switching to main thread 
} 

// inside caller method - no change here 
[X doSomething:^(Response * response) { 
    NSLog (@"%@", [response msg]); 
} 

代碼工作完美。爲什麼會發生?這是在沒有ARC的Xcode中構建的。

編輯:有人提到關於初始化。這是我的錯誤,上面寫的不完全是我的代碼,我錯誤地複製了init方法。這是我的init方法:

- (instancetype) initWithData:(NSDictionary *)freshData { 
    NSParameterAssert(freshData); // make sure not nil 
    self = [super init]; 
    if (self) { 
     kdata = freshData; 
    } 
    return self; 
} 

回答

1

問題是當您調用「異步」時,對象get被釋放。 你聲明你的對象的方式被添加到自動釋放池,因爲控件不等待'異步'完成,控制返回到達函數'doSomething'的末尾並釋放它添加到自動釋放池的本地對象,之後的內存位置用於其他數據,這就是你看到混亂的數據。 我認爲通過在聲明前添加__block說明符,您可以指示代碼強制在以下塊中捕獲該對象,並在該塊完成執行時釋放它。試一試。

// inside a networking class X implementation 
    - (void) doSomething:(completionBlock)completion { 
     NSDictionary * json = // get from networking function, which will always have key "msg". 
     __block Response * responseObj = [[Response alloc] initWithJson:json]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      if (completion != nil) { completion (responseObj); } 
     }); 
    } 
+0

是的,我懷疑這個對象在我的異步調用被調用時已經被釋放,但我不知道如何解決這種情況。我明白了!我從來不知道這個'__block'命令。這個命令是否也會增加計數器,因爲它在塊中被「複製」,所以它不會在外部函數結束時被釋放? –

+0

是的,它會增加保留計數,並在塊的執行結束時遞減,屆時如果保留計數爲零,它將在下一個垃圾收集器週期中與其他自動釋放內存一起釋放。但是你提到你已經嘗試過使用'retain'手動操作,它不起作用,這讓我對它產生懷疑,所以這就是我要求試用它的原因。 –

+0

理解你的解釋之後,我理解當然我的'retain'方法不起作用,因爲我使用塊線程內的'retain'將對​​象傳遞給完成塊lol。到那時,即使在調用retain方法之前,對象可能已經被釋放,所以我保留了一個釋放的內存塊。 xD順便說一句,謝謝,這工作! –

1
- (id)initWithJson:(NSDictionary *)data { kdata = data; } 

您需要的時候調用超init這裏並返回self。 開始學習基礎知識。

+0

您有一點。但是,在我的原始代碼中,init函數已經是正確的了。我只是錯誤地將其複製到問題。所以我編輯它以反映正確的狀態。 –

相關問題