惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
腾讯CDC
Y
Y Combinator Blog
L
LangChain Blog
B
Blog
U
Unit 42
P
Proofpoint News Feed
G
Google Developers Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
WordPress大学
WordPress大学
月光博客
月光博客
Vercel News
Vercel News
雷峰网
雷峰网
T
The Blog of Author Tim Ferriss
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 叶小钗

博客园 - Orca

IOS程序崩溃报告管理解决方案(Crashlytics 在2014-09-24) 商业模式到底是什么?(转载) 实现不同行业不同职位的体验 OpenGL中主要的4种矩阵如何理解(使用类比方式以便于方便理解) OpenGL ES入门指南-原理(转载) 从定时任务看NSRunLoop和Autorelease(转载) 多线程:NSThread和runloop(转载) iOS内存暴增问题追查与使用陷阱 (转载) 需要时显示——论App中的功能可见性(转) 如果爱,请深爱:10大iOS开发者最喜爱的类库(转载) 完整地翻译了一份ASIHttpRequest的文档 (转载) IOS 多线程编程指南【中文完整翻译版】(转载) IOS 5 Storyboard全解析-第二部分(转载) IOS 5 Storyboard全解析-第一部分(转载) IOS 学习总结(不断更新中) IOS 5 中@synthesize window = _window是什么意思呢 IOS 5 中为什么outlet 输出口总是设定成弱类型(weak)呢 浅谈iOS MVC 学习基础(转) iOS里的MVC(转)
NSTimer与Run loop Modes(转载)
Orca · 2012-12-04 · via 博客园 - Orca

一.NSRunLoop

在Cocoa中,每个线程(NSThread)对象中内部都有一个run loop(NSRunLoop)对象用来循环处理输入事件,处理的事件包括两类,一是来自Input sources的异步事件,一是来自Timer sources的同步事件;
run Loop在处理输入事件时会产生通知,可以通过Core Foundation向线程中添加run-loop observers来监听特定事件,以在监听的事件发生时做附加的处理工作。

每个run loop可运行在不同的模式下,一个run loop mode是一个集合,其中包含其监听的若干输入事件源,定时器,以及在事件发生时需要通知的run loop observers。运行在一种mode下的run loop只会处理其run loop mode中包含的输入源事件,定时器事件,以及通知run loop mode中包含的observers。
Cocoa中的预定义模式有:

  • Default模式
    定义:NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation)
    描述:默认模式中几乎包含了所有输入源(NSConnection除外),一般情况下应使用此模式。
  • Connection模式
    定义:NSConnectionReplyMode(Cocoa)
    描述:处理NSConnection对象相关事件,系统内部使用,用户基本不会使用。
  • Modal模式
    定义:NSModalPanelRunLoopMode(Cocoa)
    描述:处理modal panels事件。
  • Event tracking模式
    定义:UITrackingRunLoopMode(iOS) NSEventTrackingRunLoopMode(cocoa)
    描述:在拖动loop或其他user interface tracking loops时处于此种模式下,在此模式下会限制输入事件的处理。例如,当手指按住UITableView拖动时就会处于此模式。
  • Common模式
    定义:NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation)
    描述:这是一个伪模式,其为一组run loop mode的集合,将输入源加入此模式意味着在Common Modes中包含的所有模式下都可以处理。在Cocoa应用程序中,默认情况下Common Modes包含default modes,modal modes,event Tracking modes.可使用CFRunLoopAddCommonMode方法想Common Modes中添加自定义modes。

获取当前线程的run loop mode

NSString* runLoopMode = [[NSRunLoop currentRunLoop] currentMode];

二.NSTimerNSURLConnectionUITrackingRunLoopMode

NSTimer与NSURLConnection默认运行在default mode下,这样当用户在拖动UITableView处于UITrackingRunLoopMode模式时,NSTimer不能fire,NSURLConnection的数据也无法处理。
NSTimer的例子:
在一个UITableViewController中启动一个0.2s的循环定时器,在定时器到期时更新一个计数器,并显示在label上。

-(void)viewDidLoad
{
    label =[[[UILabel alloc]initWithFrame:CGRectMake(10, 100, 100, 50)]autorelease];
    [self.view addSubview:label];
    count = 0;
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: 1
                                                      target: self
                                                    selector: @selector(incrementCounter:)
                                                    userInfo: nil
                                                     repeats: YES];
}

- (void)incrementCounter:(NSTimer *)theTimer
{
    count++;
    label.text = [NSString stringWithFormat:@"%d",count];
}

在正常情况下,可看到每隔0.2s,label上显示的数字+1,但当你拖动或按住tableView时,label上的数字不再更新,当你手指离开时,label上的数字继续更新。当你拖动UItableView时,当前线程run loop处于UIEventTrackingRunLoopMode模式,在这种模式下,不处理定时器事件,即定时器无法fire,label上的数字也就无法更新。
解决方法,一种方法是在另外的线程中处理定时器事件,可把Timer加入到NSOperation中在另一个线程中调度;还有一种方法时修改Timer运行的run loop模式,将其加入到UITrackingRunLoopMode模式或NSRunLoopCommonModes模式中。

[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

NSURLConnection也是如此,见SDWebImage中的描述,以及SDWebImageDownloader.m代码中的实现。修改NSURLConnection的运行模式可使用scheduleInRunLoop:forMode:方法。

NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
 NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]autorelease];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];

参考:
Threading Programming Guide – Run Loops
NSRunLoop Class Reference
NSURLConnection Class Reference
NSTimer Class Reference
CFRunLoop wiki
SDWebImage
TestButtonDown
NSTimerDoesntRunWhenMenuClicked