NSoperation e NSnotificationCenter no fio principal
-
21-09-2019 - |
Pergunta
Eu tenho uma NSOPERAÇÃO. Quando terminar, disparo um centro do NSNotification para informar o programa que a NSOPERATR está concluída e atualizando a GUI.
Para meus ouvintes compreensivos, a NSNotification não será executada no segmento principal porque a NSOPERATR não está no encadeamento principal.
Como posso fazê -lo para que os ouvintes corram no tópico principal quando eu disparo meu evento?
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self];
Solução
Você pode usar performSelectorOnMainThread:withObject:waitUntilDone:
Com o uso de um método auxiliar, de maneira semelhante ao exemplo a seguir.
.....
[self performSelectorOnMainThread:@selector(fireNotification) withObject:nil waitUntilDone:YES];
...
- (void)fireNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self];
}
Se você não esperar até que seja feito, precisará considerar os casos em que outros threads podem se referir ao objeto que já poderia ser limpo antes que o thread principal seja invocado.
Outras dicas
ATUALIZAÇÃO: As filas de despacho facilitam a publicação de uma notificação no tópico principal.
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotification...];
});
Para aguardar o término dos manipuladores de notificação, basta substituir Dispatch_async pelo Dispatch_Sync.
Após a resposta de Notnoop, aqui está alguma infraestrutura que você pode usar para postar com segurança suas notificações no tópico principal sem esperando que eles terminem. Espero que alguém ache isso útil!
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
Se você está em 10.6, também pode usar setCompletionBlock:. É usado assim:
NSOperation*op= .... ;
[op setCompletionBlock:^{
dispatch_async(dispatch_get_main_queue(),^{
code to be run on the main thread after the operation is finished.
});
}];
Para introdução geral em blocos e GCD, Este artigo foi extremamente útil. Achei o GCD & SetCompletionBlock mais fácil de ler do que a NSNotification. Uma ressalva é, bem, só funciona em 10.6!
Para expandir a resposta de Danra, aqui está a versão compatível com a ARC da categoria que montei:
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