主线程上的NSOperation和NSNotificationCenter

19
我有一个NSOperation。 当它完成时,我会发出一个NSNotificationCenter来通知程序该NSoperation已经完成并更新GUI。
据我所知,对NSNotificationCenter的监听器不会在主线程上运行,因为NSOperation不在主线程上。
当我触发事件时,如何使监听器在主线程上运行?
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self]; 
4个回答

41

更新: 使用调度队列可以轻松地在主线程上发布通知。

dispatch_async(dispatch_get_main_queue(),^{
   [[NSNotificationCenter defaultCenter] postNotification...];
});
为了等待通知处理程序完成,只需将dispatch_async替换为dispatch_sync即可。
根据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

23

您可以使用 performSelectorOnMainThread:withObject:waitUntilDone: 与使用帮助方法类似,就像以下示例:

.....
[self performSelectorOnMainThread:@selector(fireNotification) withObject:nil waitUntilDone:YES];
...

- (void)fireNotification {
  [[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self]; 
}

如果您不等待操作完成,您需要考虑其他线程可能在主线程调用之前引用的对象已经被清理的情况。


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的一般介绍,这篇文章非常有帮助。我发现GCD和setCompletionBlock比NSNotification更易读。唯一的注意点是,它只能在10.6上运行!


1
如果使用了“iPhone”标签,那么提问者可能不在10.6上。 - Alex Reynolds

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接