我设置了一个UITableView,它应该在从网站加载某些数据后显示一些信息。为了更新UI和完成加载的数据,我使用了以下方法:
performSelectorOnMainThread:@selector(getBooks) withObject:nil waitUntilDone:YES
-(void)getBooks{
NSMutableString *theURL = [[NSMutableString alloc] initWithString:@"theURL"];
[theURL appendFormat:@"&UserID=%@", _appDel.userID];
NSLog(@"%@\n", theURL);
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:theURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"An Error Occured" message:@"Couldn't make a connection to the server. Please make sure your internet connection is on." delegate:self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
[alert show];
}
} 但是,当我运行此程序时,程序继续执行,尽管我将waitUntilDone设置为YES。我的印象是waitUntilDone会等待选择器,直到它完成执行。任何人都可以解释可能出现的问题吗?谢谢你的帮助。
答案 0 :(得分:2)
如果你看the Apple docs,他们会说:
等待
一个布尔值,指定当前线程是否阻塞,直到在接收器上执行指定的选择器之后 主线程。指定YES以阻止此线程;否则,请指定NO 让这个方法立即返回。如果当前线程也是 主线程,并为此参数指定YES,即消息 立即交付和处理。
因此,如果您从主线程调用此方法,则会立即执行。
但是,这样做真的没有意义。如果您在主线程上,并希望在主线程上执行getBooks
,那么就这样做:
-(void) viewDidLoad {
[super viewDidLoad];
[self getBooks];
}
如果您向我们展示您在getBooks
内实际执行的操作,我们可能会提供更多帮助。例如,如果getBooks
正在发出远程HTTP请求,要获取在线图书数据,那么您根本不想使用performSelectorOnMainThread:
。你想在后台线程上运行它,然后只有在网络请求完成后才回调主线程来更新你的UI。
<强>更新强>
有很多方法可以检索网页内容。如果您要直接使用NSURLRequest
,那么您应该确保此类实现NSURLConnectionDelegate
协议:
@interface MyViewController: UIViewController<NSURLConnectionDelegate> {
然后实现其方法per the Apple example
- (void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response
{
// this method is called when the server has determined that it
// has enough information to create the NSURLResponse
// it can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
[receivedData setLength:0];
}
- (void) connection:(NSURLConnection *) connection didReceiveData:(NSData *)data
{
// append the new data to the receivedData
[receivedData appendData:data];
}
- (void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error
{
// release the connection, and the data object
[connection release];
[receivedData release];
// inform the user
UIAlertView* netAlert = [[UIAlertView alloc] initWithTitle:@"" message:@"Oops!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[netAlert show];
[netAlert release];
}
- (void) connectionDidFinishLoading:(NSURLConnection *) connection
{
if ([receivedData length] > 0) {
// do something with the data, and then tell the UI to update
[self performSelectorOnMainThread: @selector(updateUI) withObject: nil waitUntilDone: NO];
}
// release the connection, and the data object
[connection release];
[receivedData release];
receivedData = nil;
}
-(void) updateUI {
[tableView reloadData];
someLabel.text = @"New Status";
// whatever else needs updating
}
注意:上面的代码是在ARC引入之前编写的。如果您的项目使用ARC,则需要使用release
调用删除所有这些行。不过,建议您确保receivedData
被正确保留,因为您将在多个来电中使用它。
另一个注意事项:NSURLConnection
可以多种方式使用。它可以同步使用,也可以异步使用。并且,您始终可以决定启动它的线程。如果你保留这个:
-(void) viewDidLoad {
[super viewDidLoad];
[self getBooks];
}
然后将在主线程上启动连接。但是,它仍然是异步操作。换句话说,在网络请求完成之前,它不会阻止viewDidLoad
。但是,如果用户在下载发生时(如滚动)将要对UI执行任何重要操作,那么您可能会发现UI的响应性降低。如果是这种情况,您可以want to do this,或强制网络操作到后台线程。为此,请使用以下命令启动它:
-(void) viewDidLoad {
[super viewDidLoad];
[self performSelectorInBackground: @selector(getBooksInBackground) withObject: nil];
}
然后,我上面的NSURLConnectionDelegate
代码的唯一问题是后台线程的活动时间不足以通过委托回调传递响应。为了让它保持活力,
-(void) getBooksInBackground {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[self getBooks];
CFRunLoopRun(); // Avoid thread exiting
[pool release];
}
然后您必须将此调用添加到connectionDidFinishLoading
和connection:didFailWithError
的末尾以清除此后台运行循环:
CFRunLoopStop(CFRunLoopGetCurrent());
再次,旧代码,所以使用
@autoreleasepool {
}
ARC的。