如何处理setDelegate:使用多个线程时

时间:2010-07-01 13:45:18

标签: iphone objective-c cocoa multithreading

我遇到过使用第三方库的问题,我不确定解决它的常见模式是什么。

我正在使用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

关于“正常”解决方案的所有建议对此表示赞赏!

由于

约瑟夫

2 个答案:

答案 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];
祝你好运,竞争条件让我们最好!