NSTimer与NSRunLoop
来源:互联网 发布:csgo残局 知乎 编辑:程序博客网 时间:2024/04/25 23:37
NSTimer使用方法
参考How do I use NSTimer?,使用NSTimer的方式如下:
scheduled timer & 使用 selector,这是最常用的方式
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0 target: self selector:@selector(onTick:) userInfo: nil repeats:NO];
self-scheduled timer
NSDate *d = [NSDate dateWithTimeIntervalSinceNow: 60.0];NSTimer *t = [[NSTimer alloc] initWithFireDate: d interval: 1 target: self selector:@selector(onTick:) userInfo:nil repeats:YES];NSRunLoop *runner = [NSRunLoop currentRunLoop];[runner addTimer:t forMode: NSDefaultRunLoopMode];[t release];
上面的方法,创建了一个在自定义的时间开始的timer(一分钟以后),每一秒重复一次。
unscheduled timer & using invocation
NSMethodSignature *sgn = [self methodSignatureForSelector:@selector(onTick:)];NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];[inv setTarget: self];[inv setSelector:@selector(onTick:)];NSTimer *t = [NSTimer timerWithTimeInterval: 1.0 invocation:inv repeats:YES];
再使用如下的方式启动定时器:
NSRunLoop *runner = [NSRunLoop currentRunLoop];[runner addTimer: t forMode: NSDefaultRunLoopMode];
NSTimer与NSRunLoop
请参考:
- NSTimer你真的会用了吗
- IOS開發中NSRunloop跟NSTimer的問題
- Cocoa深入学习:NSOperationQueue、NSRunLoop和线程安全
使用如下的方式创建一个NSTimer
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(printMessage:) userInfo:nil repeats:YES]; }
這個時候如果我們在界面上滾動一個scrollview,那麼我們會發現在停止滾動前,控制台不會有任何輸出,就好像scrollView在滾動的時候將timer暫停了一樣,在查看相應文檔後發現,這其實就是runloop的mode在做怪。
runloop可以理解為cocoa下的一種消息循環機制,用來處理各種消息事件,我們在開發的時候並不需要手動去創建一個runloop,因為框架為我們創建了一個默認的runloop,通過[NSRunloop currentRunloop]我們可以得到一個當前線程下面對應的runloop對象,不過我們需要注意的是不同的runloop之間消息的通知方式。
接著上面的話題,在開啟一個NSTimer實質上是在當前的runloop中註冊了一個新的事件源,而當scrollView滾動的時候,當前的MainRunLoop是處於UITrackingRunLoopMode的模式下,在這個模式下,是不會處理NSDefaultRunLoopMode的消息(因為RunLoop Mode不一樣),要想在scrollView滾動的同時也接受其它runloop的消息,我們需要改變兩者之間的runloopmode.
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
簡單的說就是NSTimer不會開啟新的進程,只是在Runloop里註冊了一下,Runloop每次loop時都會檢測這個timer,看是否可以觸發。當Runloop在A mode,而timer註冊在B mode時就無法去檢測這個timer,所以需要把NSTimer也註冊到A mode,這樣就可以被檢測到。
說到這裡,在http非同步通信的模塊中也有可能碰到這樣的問題,就是在向伺服器非同步獲取圖片數據通知主線程刷新tableView中的圖片時,在tableView滾動沒有停止或用戶手指停留在屏幕上的時候,圖片一直不會出來,可能背後也是這個runloop的mode在做怪,嘿嘿。
NSTimer会保留其目标对象如何处理?
内容来自《Effective Objective-C 2.0》
由于计时器会保留其目标对象,所以反复执行任务通常会导致应用程序出问题。也就是说设置成重复执行模式的那种计时器,很容易引入“保留环”。请看如下代码:
#import <Foundation/Foundation.h>@interface EOCClass : NSObject- (void)startPolling;- (void)stopPolling;@end@implementation EOCClass{ NSTimer *_pollTimer;}- (instancetype)init{ return [super init];}- (void)dealloc{ [_pollTimer invalidate];}- (void)stopPolling{ [_pollTimer invalidate]; _pollTimer = nil;}- (void)startPolling{ _pollTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(p_doPoll) userInfo:nil repeats:YES];}- (void)p_doPoll{}@end
创建计时器的时候,由于目标对象是self,所以要保留此实例。然而,因为计时器是用实例变量存放的,所以实例也保留了计时器。于是,就产生了“保留环”。
这个问题可以通过“块”来解决。虽然计时器当前并不支持块,但是可以用下面这段代码为其添加功能:
@interface NSTimer (EOCBlockSupport)+ (NSTimer *)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats;@end@implementation NSTimer (EOCBlockSupport)+ (NSTimer *)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)())block repeats:(BOOL)repeats{ return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(eoc_blockInvoke:) userInfo:[block copy] repeats:repeats];}+ (void)eoc_blockInvoke:(NSTimer *)timer{ void (^block)() = timer.userInfo; if (block) { block(); }}@end
计时器现在的target是NSTimer类对象,这是个单例,因此计时器是否会保留它,其实都无所谓。此处依然有保留环,然而因为类对象(class object)无须回收,所以不用担心。
这套方案本身并不能解决问题,但它提供了解决问题所需的工具。修改刚才那段有问题的范例代码,使用分类中的eoc_scheduledTimerWithTimeInterval方法来创建计时器:
- (void)startPolling{ _pollTimer = [NSTimer eoc_scheduledTimerWithTimeInterval:5.0 block:^{ [self p_doPoll]; } repeats:YES];}
仔细看代码,会发现还是有保留环。因为块捕捉了self变量,所以块要保留实例。而计时器又通过userInfo参数保留了快。最后,实例本身还要保留计时器。不过,只要用weak引用,即可打破保留环:
- (void)startPolling{ __weak EOCClass *weakSelf = self; _pollTimer = [NSTimer eoc_scheduledTimerWithTimeInterval:5.0 block:^{ EOCClass *strongSelf = weakSelf; [strongSelf p_doPoll]; } repeats:YES];}
- NSTimer 与 NSRunLoop Modes
- NSTimer与NSRunLoop
- NSTimer与NSRunLoop
- NSTimer与NSRunLoop的关系分析
- IOS开发——NSRunloop与NSTimer
- 一些关于NSRunLoop与NSTimer的知识
- NSTimer与NSRunLoop的关系分析
- 自己调研的一些关于NSRunLoop与NSTimer的知识
- NSRunloop与NSTimer在IOS开发中如何工作
- iOS开发中 NSRunloop 与NSTimer的问题
- nstimer,nsrunloop,nsthread,nstask
- NSRunLoop和nstimer
- NSRunloop 和 NSTimer
- NSRunLoop和NSTimer
- NSRunLoop 和 NSTimer
- NSTimer NSRunloop的简单认识
- ios-NSRunLoop以及定时器NSTimer-理解
- 关于NSRunLoop和NSTimer的深入理解
- Hashmap实现原理
- keil 5.16a使用
- 图的遍历(PTA题目解答)
- Servlet 工作原理解析
- mac装机
- NSTimer与NSRunLoop
- Java接口实验,计算矩形和圆的周长和面积
- 2010 Asia Regional Tianjin Site —— Online Contest (线段树二维转一维,2-SAT,floyed变形)hdu3621-3631
- android学习笔记(18)数据库sqlite初步
- Http协议详解2
- POJ 1860 Currency Exchange
- Python使用C语言生成的库
- CocoaPods升级
- springboot 试用笔记