我正在开发一个UIView,其中必须有一个访问远程数据库的进程,并且必须使用检索到的结果更新UITableView。
为此,我计划使用NSTimer,每隔10秒运行一次访问数据库和检索数据的更新过程。
问题是如果我在主线程中运行它,视图将被冻结,直到检索并加载数据。
任何人都知道这是最好的方法吗?任何一个例子都是apreciated。
感谢。
编辑----
这是我用来启动的代码&停止更新过程:
-(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;
}
此外,几分钟后运行app chashes而没有错误消息。我认为这是因为内存使用或僵尸,但我已经用仪器测试,内存使用量为3.11 Mb(live)&没有僵尸。
也许是因为计时器?
感谢。
编辑--- 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;
}
可以从mainThread调用loadMessagesFromUser并访问相同的非原子对象。安静的崩溃是否可能因为这个?
从主线程调用启动和停止函数。其余的是计时器的东西。
感谢您的回复。
答案 0 :(得分:1)
启动计时器调用的方法:
if ([NSThread isMainThread]) {
// Must NOT be main thread
[self performSelectorInBackground:@selector(listMustBeUpdated:) withObject:nil];
return;
}
我没有注意到您发布的代码中有任何内容会导致崩溃但没有输出。我想象它在其他代码中。如果你想问这个问题,我建议发布另一个问题,提供更多关于你究竟在做什么的信息。
答案 1 :(得分:1)
首先要考虑的是如何访问远程数据库。如果您只是通过HTTP获取URL的内容,则可以使用NSURLConnection
异步获取内容。您可以将其设置为使用委托或完成块。它将执行HTTP请求并在响应到达时通知您,并且您不必担心线程。
如果您使用某个库来访问远程数据库,并且该库尚未提供异步API,那么您需要担心阻塞主线程。您可以使用Grand Central Dispatch(GCD)来避免阻塞主线程。
在视图控制器中,您需要一个GCD队列,一个NSTimer
和一个标志,以确保如果一个数据库请求已在运行且需要很长时间,则不会启动第二个数据库请求。 / p>
@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;
});
}
答案 2 :(得分:0)
我在this博客中找到了解决方案。
@interface Test : NSObject
{
NSTimer *_timer;
NSRunLoop *_runLoop;
NSThread *_timerThread;
}
- (void)suspendTimer;
- (void)resumeTimer;
- (void)timerFireMethod:(NSTimer*)timer;
@end // End Test interface
@implementation Test
- (void)suspendTimer
{
if(_timer != nil)
{
[_timer invalidate];
[_timer release];
_timer = nil;
CFRunLoopStop([_runLoop getCFRunLoop]);
[_timerThread release];
_timerThread = nil;
}
}
- (void)_startTimerThread
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
_runLoop = [NSRunLoop currentRunLoop];
_timer = [[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerFireMethod:)
userInfo:nil
repeats:YES] retain];
[_runLoop run];
[pool release];
}
- (void)resumeTimer
{
if(_timer == nil)
{
_timerThread = [[NSThread alloc] initWithTarget:self
selector:@selector(_startTimerThread)
object:nil];
[_timerThread start];
}
}
- (void)timerFireMethod:(NSTimer*)timer
{
// ...
}
@end // End Test implementation
我也在使用它,它对我来说很好。