[英]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.