我正在尝试使用KVO在我的UILabel和我的对象数据之间建立一个小的绑定系统。如果我的UI更改,我的数据必须更改,如果我的数据更改,我的UI应该刷新以显示新值。
我遇到的最大问题是我需要使用__bridge_retained void * - 或CFBridgingRetain()将自定义对象强制转换为void *(context) - 但我不知道应该在哪里调用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)];
}
答案 0 :(得分:1)
传统上,这个用户使用静态char *作为上下文参数,以便区分不同的observeValueForKeyPath
消息。也就是说,应该可以在尝试时做点什么。
我建议从自定义对象切换到Core Foundation,在那里您可以明确地拥有内存管理。因此,我建议将PLSObjectPath
更改为CFDictionary。您可以先创建一个NSDictionary,然后使用适当的强制转换将其“传输”到CF域,并将该CFDictionary对象传递给上下文(现在是一个保留的CF对象)。将它observeValueForKeyPath
重新转换为CFDictionary,正确地将它转换为NSDictionary,使用它,如果你正确地完成了ARC,它应该被释放。这是一个很好理解的范例 - 将对象移入和移出ARC。
另一种方法是使用静态NSMutableDictionary,并使用上下文指针转到int
值,转换为NSNumber时,该值是字典的关键。如果所有KVO都出现在主线程上,则不需要保护字典,但如果没有,则需要将对字典的所有访问权限置于串行调度队列后面,该队列强制在一个线程上进行串行访问。
答案 1 :(得分:0)
内存泄漏来自[obj.theObj removeObserver:self forKeyPath:obj.thePath]。您正在删除观察者,但由于系统不将上下文视为对象,因此不会释放它。此外,您无法从观察对象本身获取上下文。
在某些时候,您需要停止所有观察以允许您的视图被取消分配。这应该发生在viewDid(Will)消失:为了能够在那时释放PLSObjectPath:s,你需要将它们存储在某个地方,可能是NSMutableArray。如果您仍然为此目的存储这些,则不需要使用__bridge_retained。此外,在这种情况下,您的PLSObjectPath:s可能/应该包含指向其他上下文的void *。这样,您可以在observeValueForKeyPath:ofObject:change:context:。
中保存PLSObject的创建。只是评论,你应该从viewWill(Did)出现KVO:而不是来自viewDidLoad。它为您提供了更好的KVO启动/停止管理,并且当您的视图不在屏幕上时也消除了不必要的观察。