繁体   English   中英

UITableView没有响应performSelectorOnMainThread

[英]UITableView not responding to performSelectorOnMainThread

我设置了一个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会等待选择器,直到它完成执行。 任何人都可以解释可能出现的问题吗? 谢谢你的帮助。

如果您查看Apple文档 ,他们会说:

等待

一个布尔值,指定当前线程是否阻塞,直到在主线程上的接收器上执行指定的选择器之后。 指定YES以阻止此线程; 否则,指定NO以使此方法立即返回。 如果当前线程也是主线程,并且为此参数指定YES,则会立即传递和处理消息。

因此,如果从主线程调用此方法,则会立即执行。

但是,这样做真的没有意义。 如果你在主线程上,并希望在主线程上执行getBooks ,那么就这样做:

-(void) viewDidLoad {
   [super viewDidLoad];
   [self getBooks];
}

如果您向我们展示了您在getBooks实际所做的getBooks ,我们可能会提供更多帮助。 例如,如果getBooks正在发出远程HTTP请求,要获取在线图书数据,那么您根本不想使用performSelectorOnMainThread: . 您希望在后台线程上运行该操作,然后只有在网络请求完成后才回调主线程以更新您的UI。


更新:

有很多方法可以检索Web内容。 如果您要直接使用NSURLRequest ,那么您应确保此类实现NSURLConnectionDelegate协议:

@interface MyViewController: UIViewController<NSURLConnectionDelegate> {

然后根据Apple示例实现其方法

- (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的响应性降低。 如果是这种情况,您可能要么这样做 ,要么强制网络操作到后台线程。 要做到这一点,请启动它:

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

然后,您必须将此调用添加到connectionDidFinishLoadingconnection:didFailWithError以清除connection:didFailWithError运行循环:

    CFRunLoopStop(CFRunLoopGetCurrent());

再次,旧代码,所以使用

    @autoreleasepool {
    }

对于ARC。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM