[英]NSTimer's action method is not being called
我有以下代碼來創建一個NSTimer
,它應該在每次觸發時更新一個標簽:
.h文件
@interface Game : UIViewController
{
NSTimer *updateTimer;
UILabel *testLabel;
int i;
}
-(void)GameUpdate;
@end
.m文件
@implementation Game
-(void)GameUpdate
{
i++;
NSString *textToDisplay = [[NSString alloc] initWithFormat:@"Frame: %d", i];
[testLabel setText:textToDisplay];
NSLog(@"Game Updated");
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];
}
//other methods (viewDidUnload, init method, etc.)
@end
當我運行它時,頂部會出現一個標簽,上面寫着“ 0”,但沒有變化。 這使我相信我錯過了如何設置NSTimer
。 我錯過了什么?
我使用斷點和(如您所見)日志記錄來查看該方法是否真正在運行,而不是其他一些錯誤。
我遇到了類似的問題,並且它具有與運行循環相關的根本原因。 值得注意的是,當您使用代碼安排計時器時:
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];
計時器將與當前線程的runLoop一起安排。 在您的情況下,因為您是在viewDidLoad內進行此調用的,所以它是主線程,因此您很高興。
但是,如果使用非主線程調度計時器,則它將在runLoop上針對該線程而不是主線程進行調度。 沒關系,但是在輔助線程上,您負責創建和啟動初始運行循環,因此,如果您尚未執行此操作,則永遠不會調用回調。
解決方案是為您的輔助線程啟動runLoop,或者將計時器啟動分派到主線程上。
派遣:
dispatch_async(dispatch_get_main_queue(), ^{
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];
});
要啟動運行循環:
使用您選擇的API創建線程后,調用CFRunLoopGetCurrent()為該線程分配初始運行循環。 將來對CFRunLoopGetCurrent的任何調用都將返回相同的運行循環。
CFRunLoopGetCurrent();
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];
您的回調必須具有以下簽名:
-(void)GameUpdate:(NSTimer *)timer
這是明確地在文檔中。 和@selector()引用,當你設置定時器應@selector(GameUpdate:)
(注意尾隨:
)。
試試看
以防萬一有人偶然發現這一點,我想指出一點:
[[NSString alloc] initWithFormat:@"Frame: %d", i];
需要內存管理。
安全地替換為:
[NSString stringWithFormat:@"Frame: %d", i];
具有相同的效果,但不需要內存管理。
PS:在撰寫本文時,我無法對原始帖子發表評論,因此,我將其添加為答案。
編輯:正如亞當·懷特在下面指出的那樣,這與ARC的廣泛使用不再相關。
我在NSTimer中遇到了一些不同的問題-UITableView滾動期間忽略了計划的方法調用。 計時器已從主線程啟動。 將計時器顯式添加到主運行循環可解決此問題。
[[NSRunLoop mainRunLoop] addTimer:playbackTimer forMode:NSRunLoopCommonModes];
在這里找到解決方案https://stackoverflow.com/a/2716605/1994889
UPD : CADisplayLink
更適合更新UI。 根據官方文檔,CADisplayLink是:
表示綁定到顯示vsync的計時器的類。
可以像這樣輕松實現:
playbackTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateUI)];
[playbackTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
並像刪除
if (playbackTimer) {
[playbackTimer removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
playbackTimer = nil;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.