Objective c 时间,线程。如何创建线程化NSTimer?

Objective c 时间,线程。如何创建线程化NSTimer?,objective-c,nstimer,nsthread,Objective C,Nstimer,Nsthread,我正在开发一个UIView,其中必须有一个访问远程数据库的进程,并且必须用检索到的结果更新UITableView 为此,我计划使用一个NSTimer,它每10秒运行一次数据库访问和数据检索的更新过程 问题是,如果我在主线程中运行它,视图将被冻结,直到检索并加载数据 有人知道这是最好的方法吗?任何例子都将受到重视 谢谢 编辑---- 这是我用来启动和停止更新过程的代码: -(void)startUpdating { self.isUpdating = YES; timerLoop

我正在开发一个UIView,其中必须有一个访问远程数据库的进程,并且必须用检索到的结果更新UITableView

为此,我计划使用一个NSTimer,它每10秒运行一次数据库访问和数据检索的更新过程

问题是,如果我在主线程中运行它,视图将被冻结,直到检索并加载数据

有人知道这是最好的方法吗?任何例子都将受到重视

谢谢

编辑----

这是我用来启动和停止更新过程的代码:

-(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;
    });
}