NSOperation et NSNotificationCenter sur le thread principal
-
21-09-2019 - |
Question
J'ai un NSOperation. Une fois terminé, je tire un NSNotificationCenter pour laisser le programme savoir que le NSOperation est terminé et mettre à jour l'interface utilisateur graphique.
Pour mes auditeurs à la compréhension NSNotification ne fonctionnera pas sur le thread principal car le NSOperation n'est pas sur le thread principal.
Comment puis-je faire en sorte que les auditeurs courent sur le thread principal quand je tire mon événement?
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self];
La solution
Vous pouvez utiliser performSelectorOnMainThread:withObject:waitUntilDone:
à l'aide d'une méthode d'assistance, d'une manière 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é.
Autres conseils
Mise à jour: les files d'attente font Dispatch affichant une notification sur le thread principal très facile.
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotification...];
});
Pour attendre les gestionnaires de notification pour terminer, 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 en attendant qu'ils aient fini. Espérons que quelqu'un trouve 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
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 l'introduction générale sur les blocs et GCD, cet article a été extrêmement utile. J'ai trouvé GCD et setCompletionBlock plus facile à lire que NSNotification. Une mise en garde est, bien, il ne fonctionne que sur 10.6!
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