2009-12-29 7 views
18

Ho una NSOperation. Al termine, licenzio un NSNotificationCenter per far sapere al programma che l'operazione NS è terminata e aggiornare la GUI.NSOperation e NSNotificationCenter sulla thread principale

A mio parere, gli ascoltatori di NSNotification non verranno eseguiti sul thread principale perché NSOperation non si trova sul thread principale.

Come posso far sì che gli ascoltatori girino sul thread principale quando sparo il mio evento?

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

risposta

22

È possibile utilizzare performSelectorOnMainThread:withObject:waitUntilDone: con l'utilizzo di un metodo di supporto, in modo simile al seguente esempio.

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

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

Se non si aspetta fino a quando viene fatto, è necessario prendere in considerazione i casi in cui altri thread possono fare riferimento all'oggetto che potrebbe essere già ripulito prima che il thread principale viene invocato.

6

Se si è in 10.6, è anche possibile utilizzare setCompletionBlock:. E 'usato in questo modo:

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

Per un'introduzione generale sui blocchi e GCD, this article era estremamente disponibile. Ho trovato GCD & setCompletionBlock più facile da leggere rispetto a NSNotification. Un avvertimento è, beh, funziona solo su 10.6!

+1

Se si utilizza il tag "iPhone", il richiedente non è probabilmente il 10.6. –

+0

Hai ragione, Alex. Che stupido. – Yuji

+0

grazie per il suggerimento. –

40

Aggiornamento: Le code di invio rendono molto semplice la pubblicazione di una notifica sul thread principale.

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

di aspettare per i gestori di notifica per finire, basta sostituire dispatch_async con dispatch_sync.


seguito la risposta di notnoop, ecco alcune infrastrutture è possibile utilizzare per inviare in modo sicuro le notifiche sul thread principale senza attesa di loro alla fine. Spero che qualcuno lo troverà 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

Molto bello. Grazie per aver dedicato del tempo a scrivere questo codice. –

5

Per espandere sulla risposta di Danra Ecco la versione compatibile con ARC della categoria che ho messo insieme:

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 + Fili.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

Mi sembra buono. Grazie, Oliver! – Elliot