2012-11-20 46 views
10

关于this question我想知道是否有什么普遍接受的逻辑关于何时使用NSNotification,在您的主线程中使用观察者vs使用GCD从后台线程向主线程分派工作?NSNotification与dispatch_get_main_queue

看来,通知 - 观察者设置你必须记住,当你的视图卸载但是你可靠地忽略了通知的时候拆掉观察者,当把任务分派给主线程时可能导致一个块被执行当视图被卸载时。

这样,在我看来,应通知提供改进的应用程序稳定性。我假设调度选项提供了我读过GCD的更好的性能?

UPDATE:

我知道,通知和调度可以合作愉快,并在某些情况下,应一起使用。我试图找出是否有特定的情况下,应该/不应该使用。

的情况为例:为什么我会选择主线程火从已分派块的通知,而不是仅仅调度主队列中的接收功能? (显然在这两种情况下接收函数会有一些变化,但最终结果看起来是一样的)。

+2

这些并不是真正相互排斥的选择。你可以使用GCD在后台线程上工作,然后切换回主线程并发送一个NSNotification(它是从它发送的同一个线程上接收的)。或者我不理解这个问题? – Kitsune

+0

@Kitsune我意识到这两个可以很愉快地一起使用,我只是好奇,如果有任何众所周知的情况下,其中一个或其他总是使用/避免。我一直在寻找GCD一直用于将工作发送到后台线程的示例,但有时会使用通知返回主线程和其他时间,GCD将用于返回到主线程。 – Endophage

+1

你知道通知是在他们发布的同一个线程上传递的吗?所以他们不能用于“返回主线程”! – Felix

回答

14

NSNotificationCentergcd & dispatch_get_main_queue()服务非常不同的目的。我没有什么“vs”是真正适用的。

NSNotificationCenter提供的应用程序的去耦不同部位的方式。例如,当Apple的Reachability示例代码中的kReachabilityChangedNotification在系统网络状态更改时发布到通知中心。然后,您可以要求通知中心致电您的选择器/调用,以便您可以响应此类事件。(Think Air Raid Siren)

gcd另一方面提供了一种快速分配工作的方法由您指定的队列。它可以让您告诉系统您的代码可以被分离和处理的位置,以便利用多线程和多核CPU。

一般(几乎总是)的通知其所发布的线程上观察到。采用一体成型的API显着的例外......

API的一个片而言,这些概念到相交是NSNotificationCenter的:

addObserverForName:object:queue:usingBlock: 

这实质上是确保一个给定的通知的简便方法在给定的线程上被观察到。虽然“usingBlock”参数给出走在幕后它使用gcd

这里是其使用的一个例子。假设某个地方在我的代码有一个NSTimer调用这个方法每秒:

-(void)timerTimedOut:(NSTimer *)timer{ 
    dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
     // Ha! Gotcha this is on a background thread. 
     [[NSNotificationCenter defaultCenter] postNotificationName:backgroundColorIsGettingBoringNotification object:nil]; 
    }); 
} 

我想用backgroundColorIsGettingBoringNotification作为一个信号,我改变我的视图控制器的视图的背景颜色。但它发布在后台线程上。那么我可以使用前面提到的API来观察,只有在主线程上。注意viewDidLoad下面的代码:

@implementation NoWayWillMyBackgroundBeBoringViewController { 
    id _observer; 
} 
-(void)observeHeyNotification:(NSNotification *)note{ 
    static NSArray *rainbow = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     rainbow = @[[UIColor redColor], [UIColor orangeColor], [UIColor yellowColor], [UIColor greenColor], [UIColor blueColor], [UIColor purpleColor]]; 
    }); 
    NSInteger colorIndex = [rainbow indexOfObject:self.view.backgroundColor]; 
    colorIndex++; 
    if (colorIndex == rainbow.count) colorIndex = 0; 
    self.view.backgroundColor = [rainbow objectAtIndex:colorIndex]; 
} 
- (void)viewDidLoad{ 
    [super viewDidLoad]; 
    self.view.backgroundColor = [UIColor redColor]; 
    __weak PNE_ViewController *weakSelf = self; 
    _observer = [[NSNotificationCenter defaultCenter] addObserverForName:backgroundColorIsGettingBoringNotification 
                    object:nil 
                    queue:[NSOperationQueue mainQueue] 
                   usingBlock:^(NSNotification *note){ 
                    [weakSelf observeHeyNotification:note]; 
                   }]; 
} 
-(void)viewDidUnload{ 
    [super viewDidUnload]; 
    [[NSNotificationCenter defaultCenter] removeObserver:_observer]; 
} 
-(void)dealloc{ 
    [[NSNotificationCenter defaultCenter] removeObserver:_observer]; 
} 

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{ 
    return (interfaceOrientation == UIInterfaceOrientationPortrait); 
} 
@end 

这个API的主要优点似乎是你的观察块将在postNotification...通话过程中调用。如果您使用的是标准的API,并实现observeHeyNotification:类似下面的就没有保证了执行您的调度块之前,这将是多么长:

-(void)observeHeyNotification:(NSNotification *)note{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     // Same stuff here... 
    }); 
} 

当然,在这个例子中,你可以根本就没有上张贴的通知后台线程,但如果您使用的框架不能保证在哪个线程上发布通知,这可能会派上用场。

+0

谢谢你,优秀的解释。为了澄清,'addObserverForName'确保块在绑定观察者的同一个线程中执行(就像在同一个线程中,您称为'addObserverForName')? – Endophage

+0

@Endophage号“addObserverForName:object:queue:usingBlock:”的第三个参数是一个“NSOperationQueue”。该块在传入的'NSOperationQueue'表示的线程/队列上执行。在答案示例代码中,我使用'mainQueue'类方法获得对主队列/线程的引用。 – NJones

+2

啊明白了。因此,实际上可以通过在设置观察者时传递适当的'NSNotificationQueue'来在不同的线程上接收Notification。 – Endophage