如何处理setDelegate:使用多线程时 [英] How to handle setDelegate: when using multipe threads

查看:135
本文介绍了如何处理setDelegate:使用多线程时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用第三方库时遇到了一个问题,并且不确定要解决的通用模式是什么.

I've come across an issue with using a third party library, and am not sure what the common pattern to solve it is.

我正在使用asi-http-request类,该类使用线程异步获取http对象.

I'm using the asi-http-request class, which fetches http objects asynchronously using a thread.

在我的对象dealloc()方法中,我这样做

In my objects dealloc() method, I do

[request setDelegate:nil];
[request release];

但是,有时在发生这种情况后仍会委托该代表. (我可以看到这种情况何时发生,请求对象的委托字段为nil.)如果委托已经被销毁,这有时会导致崩溃.

However the delegate is sometimes still called after this has happened. (I can see when this happens the delegate field of the request object is nil.) This sometimes causes a crash if the delegate has been destroyed already.

我认为这是比赛条件.来自ASIHTTPRequest的调用委托的代码如下:

I believe this is a race condition. The code from ASIHTTPRequest that calls the delegate looks like this:

// 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已被调用(但未完成),则会发生问题.

The problem happens if the performerSelectorOnMainThread has been called (but not completed) when the setDelegate call happens on the main thread.

一种解决方案是在"didFinishSelector"周围添加一个包装器,该包装器在调用选择器之前(在主线程上)检查委托是否仍为非零,但这会导致很多包装器.

One solution would be to add a wrapper around 'didFinishSelector' that checks (on the main thread) that the delegate is still non-nil before calling the selector, but this would result in a lot of wrappers.

这里有一些背景:

http://groups.google.com/group/asihttprequest/browse_thread /thread/721220b9645f4a42

有关常规"解决方案的所有建议对此表示赞赏!

All suggestions on the "normal" solution for this appreciated!

谢谢

约瑟夫

推荐答案

我的原始想法(围绕"didFinishSelector"包装,该主包装在调用选择器之前检查主线程该委托是否仍为非零).正确的解决方案,正如有帮助的人在apple dev论坛上确认的那样:

My original thoughts (wrapper around 'didFinishSelector' that checks on the main thread that the delegate is still non-nil before calling the selector) turned out to be the correct solution, as confirmed by helpful folks over on the apple dev forums:

https://devforums.apple.com/message/255935#255935

为了避免担心最终会产生很多包装,我设法只创建了一个包装:

To avoid my worry of ending up with lots of wrappers, I managed to create only a single wrapper:

- (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]];
}

然后,当我想调用委托时,代码看起来像这样:

then when I want to call the delegate, the code just looks something like this:

[self callSelector:&didFinishSelector withDelegate:&delegate];

从实验和实验中我可以说出最好的一面代码分析(并假设仅从主线程调用setDelegate),这是100%安全的.通过将对象锁定放在callSelectorCallback内部,可以确保非主线程对setDelegate的调用是安全的.

As best I can tell from experiments & code analysis (and assuming setDelegate is only called from the main thread), this is 100% safe. It could be made safe for the non-main thread calls to setDelegate by taking the object lock inside callSelectorCallback.

这篇关于如何处理setDelegate:使用多线程时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆