我遇到过使用第三方库的问题,我不确定解决它的常见模式是什么。
我正在使用asi-http-request类,它使用线程异步获取http对象。
在我的对象dealloc()方法中,我做
[request setDelegate:nil];
[request release];
然而,在发生这种情况后,有时仍会调用该代表。 (我可以看到,当发生这种情况时,请求对象的委托字段为nil。)如果委托已经被销毁,这有时会导致崩溃。
我认为这是一种竞争条件。调用委托的ASIHTTPRequest代码如下所示:
// Let the delegate know we are done
if ([self didFinishSelector] && [[self delegate] respondsToSelector:[self didFinishSelector]]) {
[[self delegate] performSelectorOnMainThread:[self didFinishSelector] withObject:self waitUntilDone:[NSThread isMainThread]];
}
如果在主线程上发生setDelegate调用时调用(但未完成)performerSelectorOnMainThread,则会出现问题。
一个解决方案是在'didFinishSelector'周围添加一个包装器,它在调用选择器之前检查(在主线程上)委托仍然是非nil,但这会导致很多包装器。
这里有一些背景知识:
http://groups.google.com/group/asihttprequest/browse_thread/thread/721220b9645f4a42
关于“正常”解决方案的所有建议对此表示赞赏!
由于
约瑟夫
答案 0 :(得分:2)
我最初的想法('didFinishSelector'的封装,在调用选择器之前检查代理仍然是非零的主线程)被证明是正确的解决方案,正如有用的人在apple dev论坛上所确认的那样:
https://devforums.apple.com/message/255935#255935
为了避免担心结尾有很多包装器,我设法只创建了一个包装器:
- (void)callSelectorCallback:(SEL *)selectorPtr withTarget:(id *)targetPtr
{
id target = *targetPtr;
SEL selector = *selectorPtr;
if (!selector || !target)
return;
if ([target respondsToSelector:selector])
{
[target performSelector:selector withObject:self];
}
}
- (void)callSelector:(SEL *)selector withDelegate:(id *)target
{
if (!*selector || !*target)
return;
SEL callback = @selector(callSelectorCallback:withTarget:);
NSMethodSignature *signature = [ASIHTTPRequest instanceMethodSignatureForSelector:callback];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:callback];
[invocation setTarget:self];
[invocation setArgument:&selector atIndex:2];
[invocation setArgument:&target atIndex:3];
[invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:[NSThread isMainThread]];
}
然后当我想调用委托时,代码看起来像这样:
[self callSelector:&didFinishSelector withDelegate:&delegate];
我可以从实验和实验中得知。代码分析(假设只从主线程调用setDelegate),这是100%安全的。通过在callSelectorCallback中获取对象锁,可以使非主要线程调用setDelegate变得安全。
答案 1 :(得分:1)
为了跨线程处理对象,你几乎应该总是保留它们。基本上,
id delegate = [[self delegate] retain];
if ([self didFinishSelector] && [delegate respondsToSelector:[self didFinishSelector]]) {
[delegate performSelectorOnMainThread:[self didFinishSelector]
withObject:self
waitUntilDone:[NSThread isMainThread]];
}
[delegate release];
从技术上讲,代理可以在[self delegate]和后续保留之间解除分配,我不确定Apple的@synthesized原子访问器是否可以防止这种情况,但我相信唯一的方法是解决这个问题是在访问者中,
[[delegate retain] autorelease];
祝你好运,竞争条件让我们最好!