问题描述
我在使用第三方库时遇到了一个问题,并且不确定要解决的通用模式是什么.
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:使用多线程时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!