簡體   English   中英

KVO和上下文可能會導致內存泄漏

[英]Possible memory leak with KVO and context

我正在嘗試使用KVO在UILabel和對象數據之間建立一個小的綁定系統。 如果我的UI更改,則我的數據也必須更改,如果我的數據更改,我的UI應該刷新以顯示新值。

我遇到的最大問題是,我需要使用__bridge_retained void *(或CFBridgingRetain())將自定義對象轉換為void *(上下文),但我不知道應該在哪里調用CFBridgingRelease()。 如果在observeValueForKeyPath方法中調用它,則會收到錯誤的訪問錯誤(我猜是因為對上下文所指向的對象的引用計數為0)

// viewDidLoad
// binding my label text with a custom data object
[self bindObject:_myLabel withPath:@"text" toObject:_user path:@"name"];

-(void) bindObject:(id)uiObj withPath:(NSString *)uiPath toObject:(id)dataObj path:(NSString *)dataPath
{
    // custom object storing the object I want to bind and his path
    PLSObjectPath* op = [[PLSObjectPath alloc] init];
    op.theObj = dataObj;
    op.thePath = dataPath;
    PLSObjectPath* ob = [[PLSObjectPath alloc] init];
    ob.theObj = uiObj;
    ob.thePath = uiPath;

    /* possible leak because I don't know where to call CFBridgingRelease */
    [uiObj addObserver:self forKeyPath:uiPath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(op)];
    [dataObj addObserver:self forKeyPath:dataPath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(ob)];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{   

    PLSObjectPath *obj = (__bridge PLSObjectPath*) context;
    PLSObjectPath* pairObj = [[PLSObjectPath alloc] init];
    pairObj.theObj = object;
    pairObj.thePath = keyPath;
    // avoid infinite loop
    [obj.theObj removeObserver:self forKeyPath:obj.thePath];
    [obj.theObj setValue:change[@"new"] forKeyPath:obj.thePath];
    [obj.theObj addObserver:self forKeyPath:obj.thePath options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)(pairObj)];
}

傳統上,此用戶使用靜態char *作為上下文參數,以區分不同的observeValueForKeyPath消息。 就是說,應該可以在嘗試時做一些事情。

我建議從自定義對象切換到Core Foundation,從中可以顯式地擁有內存管理。 因此,我建議將PLSObjectPath更改為CFDictionary。 您可以首先創建一個NSDictionary,然后使用適當的強制轉換將其“轉移”到CF域,然后將該CFDictionary對象傳遞給上下文(現在是保留的CF對象)。 將它在observeValueForKeyPath重鑄到CFDictionary,將ARC正確地投射到NSDictionary,然后使用它,如果正確完成了ARC,則應該釋放它。 這都是一個很好理解的天堂-將對象移入和移出ARC。

您可以執行此操作的另一種方法是使用靜態NSMutableDictionary,然后使用上下文指針轉到一個int值,將其轉換為NSNumber時將其作為字典的鍵。 如果所有KVO都發生在主線程上,則不需要保護字典,但是如果沒有,則需要將對字典的所有訪問權限放在串行分派隊列后面,從而強制對一個線程進行串行訪問。

內存泄漏來自[obj.theObj removeObserver:self forKeyPath:obj.thePath]。 您正在刪除觀察者,但是由於系統不會將上下文視為對象,因此不會釋放它。 同樣,您也無法從被觀察對象本身獲取上下文。

在某個時候,您將需要停止所有觀察以允許重新分配視圖。 這應該從viewDid(Will)Disappear:發生。 為了能夠在那時釋放PLSObjectPath:s,您將需要將它們存儲在某個地方,可能是NSMutableArray。 如果您仍然為此存儲這些,則無需使用__bridge_retained。 同樣,在這種情況下,您的PLSObjectPath:s可能/應該包含指向另一個上下文的void *。 這樣,您可以將PLSObject的創建保存在observeValueForKeyPath:ofObject:change:context:中。

只是注釋,您應該從viewWill(Did)Appear:而不是從viewDidLoad啟動KVO。 它為您提供了更好的KVO啟動/停止管理,並且還消除了視圖不在屏幕上時的不必要觀察。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM