NSOperation и NSNotificationCenter в основном потоке
-
21-09-2019 - |
Вопрос
У меня есть NSOperation.По завершении я запускаю NSNotificationCenter, чтобы сообщить программе, что NSoperation завершена, и обновить графический интерфейс.
Насколько я понимаю, слушатели NSNotification не будут работать в основном потоке, потому что NSOperation не находится в основном потоке.
Как я могу сделать так, чтобы слушатели запускались в основном потоке, когда я запускаю свое событие?
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self];
Решение
Вы можете использовать performSelectorOnMainThread:withObject:waitUntilDone:
с использованием вспомогательного метода аналогично следующему примеру.
.....
[self performSelectorOnMainThread:@selector(fireNotification) withObject:nil waitUntilDone:YES];
...
- (void)fireNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:@"myEventName" object:self];
}
Если вы не дожидаетесь завершения, вам нужно будет рассмотреть случаи, когда другие потоки могут ссылаться на объект, который уже может быть очищен до вызова основного потока.
Другие советы
Обновлять:Очереди отправки упрощают публикацию уведомления в основном потоке.
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotification...];
});
Чтобы дождаться завершения работы обработчиков уведомлений, просто замените диспетчерскую_async на диспетчерскую_синхронизацию.
Следуя ответу notnoop, вот некоторая инфраструктура, которую вы можете использовать для безопасной публикации уведомлений в основном потоке. без ждем, пока они закончатся.Надеюсь, кто-то найдет это полезным!
НСНотификатионцентр+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
НСИнвокация+Utils.h:
@interface NSInvocation (Utils)
-(void)invokeOnMainThreadWaitUntilDone:(BOOL)wait;
@end
НСИнвокация+Utils.m:
@implementation NSInvocation (Utils)
-(void)invokeOnMainThreadWaitUntilDone:(BOOL)wait
{
[self performSelectorOnMainThread:@selector(invoke)
withObject:nil
waitUntilDone:wait];
}
@end
Если у вас версия 10.6, вы также можете использовать УстановитьКомплетионБлок:.Он используется следующим образом:
NSOperation*op= .... ;
[op setCompletionBlock:^{
dispatch_async(dispatch_get_main_queue(),^{
code to be run on the main thread after the operation is finished.
});
}];
Для общего ознакомления с блоками и НОД: Эта статья был чрезвычайно полезен.Я обнаружил, что GCD и setCompletionBlock легче читать, чем NSNotification.Одно предостережение: оно работает только на 10.6!
Чтобы расширить ответ Данры, вот совместимая с 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