您知道有多少次, 作为 iOS 开发人员, 您希望确保在主线程上发生某些方法或代理调用吗?我们尝试了无数次, 虽然它是看是简单, 但它中间还是有很多坑。
这里有一个简单的方法来保持线程安全,而不必在这个过程中编写大量代码。
在我的代码中,我通常为我的
selectors
创建一个私有代理包装方法。这样我就不会在我的类中到处乱扔逻辑了。有一个需要注意对方, 如果我的代理被调用, 这意味着我不能使它崩溃。这些包装方法类似于以下内容:
- (void)delegate_willShowViewController:(UIViewController*)controller {
if ([self.delegate respondsToSelector:@selector(pagedCellController:willShowViewController:)]) {
[self.delegate pagedCellController:self willShowViewController:controller];
}
}
这样,在代理不采用selector
的情况下,我有一种处理默认值的整洁方法,我的代码要简单得多。但是,有许多UI相关的调用应该只在主线程上执行。我有很多样板代码来处理这个问题,但是我想要一个简单的方法来自动化这个过程。
无需进一步说明,如果没有调用主线程,这里有一个C函数和C宏,它简化了主线程上的任何本地 selector
的调用。
#import <Foundation/Foundation.h>
extern NSInvocation * DNInvocationForCall(id object, SEL selector, ...);
#define FORCE_MAIN_THREAD(...) if (![NSThread isMainThread]) {
NSInvocation *inv = DNInvocationForCall(self, _cmd, ##__VA_ARGS__);
dispatch_async(dispatch_get_main_queue(), ^{
[inv invoke];
});
return;
}
#import "Defines.h"
NSInvocation * DNInvocationForCall(id object, SEL selector, ...) {
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[NSMethodSignature methodSignatureForSelector:selector]];
[inv setTarget:object];
[inv setSelector:selector];
va_list arglist;
va_start(arglist, selector);
NSInteger nextArgIndex = 2;
NSObject *arg = nil;
do {
arg = va_arg(arglist, NSObject*);
if (arg && arg != [NSNull null]) {
[inv setArgument:&arg atIndex:nextArgIndex];
}
nextArgIndex++;
} while (arg != nil);
va_end(arglist);
[inv retainArguments];
return inv;
}
这个函数 NSInvocation 对象是基于object对象,selector 。宏包装这个,并且只在不在主线程上调用它。更重要的是, 宏具有从当前方法返回的功能, 在当前线程上停止执行。现在,我需要做的就是改变我的代理包装看起来像下面这样:
- (void)delegate_willShowViewController:(UIViewController*)controller {
FORCE_MAIN_THREAD(controller)
if ([self.delegate respondsToSelector:@selector(pagedCellController:willShowViewController:)]) {
[self.delegate pagedCellController:self willShowViewController:controller];
}
}