Objective c 时间,线程。如何创建线程化NSTimer?
我正在开发一个UIView,其中必须有一个访问远程数据库的进程,并且必须用检索到的结果更新UITableView 为此,我计划使用一个NSTimer,它每10秒运行一次数据库访问和数据检索的更新过程 问题是,如果我在主线程中运行它,视图将被冻结,直到检索并加载数据 有人知道这是最好的方法吗?任何例子都将受到重视 谢谢 编辑---- 这是我用来启动和停止更新过程的代码:Objective c 时间,线程。如何创建线程化NSTimer?,objective-c,nstimer,nsthread,Objective C,Nstimer,Nsthread,我正在开发一个UIView,其中必须有一个访问远程数据库的进程,并且必须用检索到的结果更新UITableView 为此,我计划使用一个NSTimer,它每10秒运行一次数据库访问和数据检索的更新过程 问题是,如果我在主线程中运行它,视图将被冻结,直到检索并加载数据 有人知道这是最好的方法吗?任何例子都将受到重视 谢谢 编辑---- 这是我用来启动和停止更新过程的代码: -(void)startUpdating { self.isUpdating = YES; timerLoop
-(void)startUpdating
{
self.isUpdating = YES;
timerLoop = [[NSTimer scheduledTimerWithTimeInterval:10.0f target:self selector:@selector(listMustBeUpdated:) userInfo:nil repeats:YES] retain];
[[NSRunLoop mainRunLoop] addTimer: timerLoop
forMode: UITrackingRunLoopMode];
}
-(void)stopUpdating
{
self.isUpdating = NO;
[timerLoop invalidate];
[timerLoop release];
timerLoop = nil;
}
此外,运行几分钟后,应用程序会出现故障,不会出现错误消息。我认为这是因为内存使用或僵尸,但我用仪器测试过,内存使用量为3.11MB(实时),没有僵尸
也许是因为计时器
谢谢
编辑--2
好的,这就是我在整个事件中使用的代码,在滚动时仍然冻结UITableView
可能是因为它是从线程更新的,而滚动是从主线程执行的
-(void)loadMessagesFromUser:(int)userId toUserId:(int)userToId
{
fromUserId = userId;
toUserId = userToId;
messages = [OTChatDataManager readMessagesFromUser:userId toUser:userToId];
lastMessageId = [[messages getValueByName:@"Id" posY:[messages CountY]-1] intValue];
[list reloadData];
}
-(void)messagesMustBeUpdated:(id)sender
{
if ([NSThread isMainThread]) {
[self performSelectorInBackground:@selector(updateMessages:) withObject:nil];
return;
}
}
-(void)updateMessages:(id)sender
{
iSQLResult *newMessages = [OTChatDataManager readMessagesFromUser:fromUserId toUser:toUserId sinceLastMessageId:lastMessageId];
for (int a=0;a<[newMessages CountY];a++)
{
[messages.Records addObject:[newMessages.Records objectAtIndex:a]];
messages.CountY++;
}
lastMessageId = [[messages getValueByName:@"Id" posY:[messages CountY]-1] intValue];
[list reloadData];
}
-(void)startUpdating
{
self.isUpdating = YES;
timerLoop = [[NSTimer scheduledTimerWithTimeInterval:10.0f target:self selector:@selector(messagesMustBeUpdated:) userInfo:nil repeats:YES] retain];
}
-(void)stopUpdating
{
self.isUpdating = NO;
[timerLoop invalidate];
[timerLoop release];
timerLoop = nil;
}
-(void)loadMessagesFromUser:(int)userId-toUserId:(int)userToId
{
fromUserId=用户ID;
toUserId=userToId;
messages=[OTChatDataManager readMessagesFromUser:userId-toUser:userToId];
lastMessageId=[[messages getValueByName:@“Id”posY:[messages CountY]-1]intValue];
[列表重新加载数据];
}
-(无效)消息必须更新:(id)发件人
{
如果([NSThread isMainThread]){
[self-performSelectorInBackground:@selector(updateMessages:)with object:nil];
返回;
}
}
-(无效)更新消息:(id)发件人
{
iSQLResult*newMessages=[OTChatDataManager readmessages fromuser:fromUserId toUser:toUser:tourid sincellastmessageid:lastMessageId];
对于(int a=0;a启动计时器调用的方法,方法如下:
if ([NSThread isMainThread]) {
// Must NOT be main thread
[self performSelectorInBackground:@selector(listMustBeUpdated:) withObject:nil];
return;
}
我没有注意到您发布的代码中有任何内容会导致没有输出的崩溃。我可以想象这是在其他代码中。如果您想了解这一点,我建议您发布另一个问题,并提供更多关于您到底在做什么的信息。我在博客中找到了一个解决方案
我也在使用它,它的工作很好。 首先要考虑的是如何访问远程数据库。如果您只是通过HTTP获取URL的内容,可以使用<代码> NSURLCONTION/COD>异步地获取内容。您可以将其设置为使用委托或完成块。重新格式化HTTP请求并在响应到达时通知您,您不需要担心线程
如果您正在使用某个库访问远程数据库,而该库尚未提供异步API,则需要担心阻塞主线程。您可以使用Grand Central Dispatch(GCD)来避免阻塞主线程
在视图控制器中,您需要一个GCD队列、一个NSTimer
,以及一个标志,以确保如果第二个数据库请求已经在运行并且需要很长时间,则不会启动第二个数据库请求
@implementation ViewController {
dispatch_queue_t _databaseQueue;
NSTimer *_databaseTimer;
BOOL _databaseQueryIsRunning;
}
您需要清理dealloc
中的GCD队列和计时器
- (void)dealloc {
if (_databaseQueue)
dispatch_release(_databaseQueue);
[_databaseTimer invalidate];
[_databaseTimer release];
[super dealloc];
}
您可以在视图出现时启动计时器
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_databaseTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self
selector:@selector(databaseTimerDidFire:) userInfo:nil repeats:YES] retain];
}
当视图消失时,您可以停止计时器
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[_databaseTimer invalidate];
[_databaseTimer release];
_databaseTimer = nil;
}
当计时器启动时,如果GCD队列中还没有一个数据库请求正在运行,则在该队列上启动一个数据库请求
- (void)databaseTimerDidFire:(NSTimer *)timer {
if (_databaseQueryIsRunning)
return;
_databaseQueryIsRunning = YES;
// Lazily create the GCD queue.
if (!_databaseQueue)
_databaseQueue = dispatch_queue_create("MyDatabaseQueue", 0);
dispatch_async(_databaseQueue, ^{
// This block runs off of the main thread.
NSObject *response = [self performDatabaseRequest];
// UI updates must happen on the main thread.
dispatch_sync(dispatch_get_main_queue(), ^{
[self updateViewWithResponse:response];
});
_databaseQueryIsRunning = NO;
});
}
重复?我以前阅读过这篇文章来提问,以及在填写标题时加载的可能相关链接的完整列表。我尝试了此解决方案,但应用程序在提问数据库时仍然冻结:(.感谢您的回复。您不需要将计时器添加到运行循环中;这可以通过scheduledTimerWithTimeInterval:…
当然,只需检查user1132003::“loadMessagesFromUser可以从主线程调用并访问同一个非原子对象。是否有可能因此而导致安静崩溃?”。是的。使用原子属性。即使如此,如果不小心,也可能会出现问题。这在另一个项目中对我有效,但似乎不适用于此项目。谢谢。感谢详细的响应,但修改DAL类不是一个选项。访问是一个跨web服务API的远程MS SQL Server 2008数据库。连接这不是异步的,因为在大多数情况下,程序流在接收数据后必须继续。我从未提到修改数据库访问层。将所有对DAL API的调用放在performDatabaseRequest
方法中。好的,但如果应用此方法,对数据库的第二个请求会发生什么情况?我需要执行两个查询将省略databaseTimerDidFire中具有第一个条件的IE?我无法理解“我需要使用databaseTimerDidFire中的第一个条件执行两个查询”这句话您好,很抱歉误解。我的错误。我应用了您的解决方案,现在TableView仍然冻结,我在崩溃之前收到调试器发出的2个内存警告。请查看更新和最终代码。谢谢。我尝试了您的方法,但在滚动时UITableView仍然冻结。来自Apple docs:“手动编写线程创建代码是乏味的,并且可能出错,并且您应该尽可能避免它。Mac OS X和IOS通过其他API提供对并发的隐式支持。而不是自己创建一个线程,考虑使用异步API、GCD或操作对象来完成这项工作。”
- (void)databaseTimerDidFire:(NSTimer *)timer {
if (_databaseQueryIsRunning)
return;
_databaseQueryIsRunning = YES;
// Lazily create the GCD queue.
if (!_databaseQueue)
_databaseQueue = dispatch_queue_create("MyDatabaseQueue", 0);
dispatch_async(_databaseQueue, ^{
// This block runs off of the main thread.
NSObject *response = [self performDatabaseRequest];
// UI updates must happen on the main thread.
dispatch_sync(dispatch_get_main_queue(), ^{
[self updateViewWithResponse:response];
});
_databaseQueryIsRunning = NO;
});
}