我有一个NSOperation。完成后,我启动NSNotificationCenter让程序知道NS操作已完成并更新gui。
据我所知,NSNotification的监听器不会在主线程上运行,因为NSOperation不在主线程上。
当我触发事件时,如何才能使侦听器在主线程上运行?
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self];
答案 0 :(得分:41)
更新: 调度队列可以很容易地在主线程上发布通知。
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotification...];
});
要等待通知处理程序完成,只需使用dispatch_sync替换dispatch_async。
根据notnoop的回答,这里有一些基础设施,您可以使用这些基础设施在主线程上安全地发布通知,而无需等待它们完成。希望有人会觉得这很有用!
NSNotificationCenter + Utils.h:
@interface NSNotificationCenter (Utils)
-(void)postNotificationOnMainThread:(NSNotification *)notification;
-(void)postNotificationNameOnMainThread:(NSString *)aName object:(id)anObject;
-(void)postNotificationNameOnMainThread:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
@end
NSNotificationCenter + Utils.m:
@interface NSNotificationCenter (Utils_Impl) {
}
-(void)postNotificationOnMainThreadImpl:(NSNotification*)notification;
-(void)postNotificationNameOnMainThreadImpl:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
@end
@implementation NSNotificationCenter (Utils)
-(void)postNotificationOnMainThread:(NSNotification *)notification {
[notification retain];
[notification.object retain];
[self performSelectorOnMainThread:@selector(postNotificationOnMainThreadImpl:)
withObject:notification
waitUntilDone:NO];
}
-(void)postNotificationNameOnMainThread:(NSString *)aName object:(id)anObject {
[self postNotificationNameOnMainThread:aName object:anObject userInfo:nil];
}
-(void)postNotificationNameOnMainThread:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo {
[aName retain];
[anObject retain];
[aUserInfo retain];
SEL sel = @selector(postNotificationNameOnMainThreadImpl:object:userInfo:);
NSMethodSignature* sig = [self methodSignatureForSelector:sel];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:self];
[invocation setSelector:sel];
[invocation setArgument:&aName atIndex:2];
[invocation setArgument:&anObject atIndex:3];
[invocation setArgument:&aUserInfo atIndex:4];
[invocation invokeOnMainThreadWaitUntilDone:NO];
}
@end
@implementation NSNotificationCenter (Utils_Impl)
-(void)postNotificationOnMainThreadImpl:(NSNotification*)notification {
[self postNotification:notification];
[notification.object release];
[notification release];
}
-(void)postNotificationNameOnMainThreadImpl:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo {
[self postNotificationName:aName object:anObject userInfo:aUserInfo];
[aName release];
[anObject release];
[aUserInfo release];
}
@end
NSInvocation的+ Utils.h:
@interface NSInvocation (Utils)
-(void)invokeOnMainThreadWaitUntilDone:(BOOL)wait;
@end
NSInvocation的+ Utils.m:
@implementation NSInvocation (Utils)
-(void)invokeOnMainThreadWaitUntilDone:(BOOL)wait
{
[self performSelectorOnMainThread:@selector(invoke)
withObject:nil
waitUntilDone:wait];
}
@end
答案 1 :(得分:23)
您可以使用performSelectorOnMainThread:withObject:waitUntilDone:
使用辅助方法,方法与以下示例类似。
.....
[self performSelectorOnMainThread:@selector(fireNotification) withObject:nil waitUntilDone:YES];
...
- (void)fireNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self];
}
如果你不等到完成,你将需要考虑其他线程可能引用在调用主线程之前已经清理过的对象的情况。
答案 2 :(得分:6)
如果您使用的是10.6,则还可以使用setCompletionBlock:。 它的使用方式如下:
NSOperation*op= .... ;
[op setCompletionBlock:^{
dispatch_async(dispatch_get_main_queue(),^{
code to be run on the main thread after the operation is finished.
});
}];
关于块和GCD的一般介绍,this article非常有帮助。我找到了GCD& setCompletionBlock比NSNotification更容易阅读。一个警告是,它只能在10.6上运行!
答案 3 :(得分:5)
为了扩展Danra的答案,这里是我合并的类别的ARC兼容版本:
<强> NSNotificationCenter + Threads.h 强>
@interface NSNotificationCenter (Threads)
-(void)postNotificationOnMainThread:(NSNotification *)notification;
-(void)postNotificationNameOnMainThread:(NSString *)name object:(id)object;
-(void)postNotificationNameOnMainThread:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;
@end
<强> NSNotificationCenter + Threads.m 强>
@implementation NSNotificationCenter (Threads)
-(void)postNotificationOnMainThread:(NSNotification *)notification
{
[self performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:NO];
}
-(void)postNotificationNameOnMainThread:(NSString *)name object:(id)object
{
[self postNotificationNameOnMainThread:name object:object userInfo:nil];
}
-(void)postNotificationNameOnMainThread:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo
{
dispatch_async(dispatch_get_main_queue(), ^{
[self postNotificationName:name object:object userInfo:userInfo];
});
}
@end