2009-12-29 5 views
18

J'ai une NSOperation. Quand c'est fini, je tire un NSNotificationCenter pour faire savoir au programme que l'opération NS est terminée et pour mettre à jour le gui. À ma connaissance, les écouteurs de NSNotification ne s'exécuteront pas sur le thread principal car NSOperation ne se trouve pas sur le thread principal.NSOperation et NSNotificationCenter sur le thread principal

Comment puis-je faire en sorte que les écouteurs s'exécutent sur le thread principal lorsque je déclenche mon événement?

[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self]; 

Répondre

22

Vous pouvez utiliser performSelectorOnMainThread:withObject:waitUntilDone: avec l'aide d'une méthode d'assistance, de façon similaire à l'exemple suivant.

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

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

Si vous n'attendez pas avant d'être fait, vous devrez tenir compte des cas où d'autres threads peuvent se référer à l'objet qui pourrait être déjà nettoyé avant que le thread principal s'invoqué.

6

Si vous êtes sur 10.6, vous pouvez également utiliser setCompletionBlock:. Il est utilisé comme ceci:

NSOperation*op= .... ; 
[op setCompletionBlock:^{ 
    dispatch_async(dispatch_get_main_queue(),^{ 
     code to be run on the main thread after the operation is finished. 
    }); 
}]; 

Pour introduction générale sur les blocs et GCD, this article a été extrêmement utile. J'ai trouvé GCD & setCompletionBlock plus facile à lire que NSNotification. Une mise en garde est, bien, il ne fonctionne que sur 10,6!

+1

Si la balise "iPhone" est utilisée, l'asker n'est probablement pas sur 10.6. –

+0

Vous avez raison, Alex. Stupide que je suis. – Yuji

+0

merci pour le conseil. –

40

Mise à jour: Les files d'attente d'envoi rendent très facile la publication d'une notification sur le thread principal.

dispatch_async(dispatch_get_main_queue(),^{ 
    [[NSNotificationCenter defaultCenter] postNotification...]; 
}); 

attendre que les gestionnaires de notification à la fin, il suffit de remplacer dispatch_async avec dispatch_sync.


Après la réponse de notnoop, voici quelques infrastructures que vous pouvez utiliser pour poster en toute sécurité vos notifications sur le thread principal sans attendre qu'ils aient fini. J'espère que quelqu'un trouvera cela utile!

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

Très bien. Merci d'avoir pris le temps d'écrire ce code. –

5

Pour développer la réponse de Danra est ici l'ARC version conforme de la catégorie I mis en place:

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 
+0

Cela me semble bon. Merci, Oliver! – Elliot

Questions connexes