0

特定のデータが Web サイトから読み込まれると、何らかの情報を表示することになっている 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 がセレクターを待機するという印象を受けました。考えられる問題を説明できる人はいますか? 助けてくれてありがとう。

4

1 に答える 1

2

Apple docsを見ると、次のように書かれています。

待つ

指定されたセレクターがメイン スレッドのレシーバーで実行されるまで、現在のスレッドをブロックするかどうかを指定するブール値。このスレッドをブロックするには YES を指定します。それ以外の場合は、NO を指定して、このメソッドがすぐに戻るようにします。現在のスレッドがメイン スレッドでもあり、このパラメーターに YES を指定すると、メッセージはすぐに配信されて処理されます。

したがって、このメソッドをメイン スレッドから呼び出すと、すぐに実行されます。

でも、そんなことをしても全く意味がありません。メイン スレッドを使用していて、メイン スレッドで実行したい場合はgetBooks、次のようにします。

-(void) viewDidLoad {
   [super viewDidLoad];
   [self 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];
}

そして、この呼び出しを両方の最後に追加し、このバックグラウンド実行ループをクリーンアップする必要がありconnectionDidFinishLoadingますconnection:didFailWithError

    CFRunLoopStop(CFRunLoopGetCurrent());

繰り返しますが、古いコードなので、使用してください

    @autoreleasepool {
    }

アーク用。

于 2012-07-31T08:54:11.193 に答える