0

私は自分のプロジェクトで MBPogressHUD を使用しており、すべてのライブラリ、インポートなどを正しくセットアップしています。ただし、セレクター メソッドに関係しているように見えるクラッシュが発生しています。基本的にサーバーに接続する「getInfo」というメソッドがあります。HUDはボタンを押すことでトリガーされます。クラッシュに関するいくつかの調査を行った後、人々は初期化を viewWillAppear に入れるように言った。なぜなら、初期化時間の一部は、実際にタスクを実行して HUD を表示するのにかかる時間がかかるからである。

    -(void)viewWillAppear:(BOOL)animated {
    connectionHUD = [[MBProgressHUD alloc]initWithView:self.view];
    [self.view addSubview:connectionHUD];
    connectionHUD.labelText = @"Connecting";
    connectionHUD.mode = MBProgressHUDModeAnnularDeterminate;    
}

-(IBAction)connectButton:(id)sender {


    [connectionHUD showWhileExecuting:@selector(getInfo) onTarget:self withObject:nil animated:YES];
}

クラッシュ:

Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...



-(void)getInfo {

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
UserPi *newPi = [[UserPi alloc]init];
newPi.passWord = self.passTextField.text;
[defaults setObject:self.passTextField.text forKey:@"password"];
newPi.userName = self.userTextField.text;
[defaults setObject:self.userTextField.text forKey:@"username"];
newPi.ipAddress = self.ipTextField.text;
[defaults setObject:self.ipTextField.text forKey:@"ip"];
[newPi connectToServer];
NSString* newAddress = [newPi returnIP];
self.connected = newPi.connected;
[self.delegate sendIP:newAddress];
[self.delegate isConnected:self.connected];
[defaults synchronize];
[self.navigationController popToRootViewControllerAnimated:YES];


}

完全なエラー:

bool _WebTryThreadLock(bool), 0x7327740: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
1   0x3a1ffe9 WebThreadLock
2   0x4ec8ff -[UITextRangeImpl isEmpty]
3   0x4ec4db -[UITextRange(UITextInputAdditions) _isCaret]
4   0x48e7b6 -[UITextSelectionView setCaretBlinks:]
5   0x328f79 -[UIKeyboardImpl setCaretBlinks:]
6   0x3185bc -[UIKeyboardImpl setDelegate:force:]
7   0x3184ae -[UIKeyboardImpl setDelegate:]
8   0x53ff65 -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
9   0x29215b -[UINavigationController navigationTransitionView:didStartTransition:]
10  0x418961 -[UINavigationTransitionView transition:fromView:toView:]
11  0x418658 -[UINavigationTransitionView transition:toView:]
12  0x294651 -[UINavigationController _startTransition:fromViewController:toViewController:]
13  0x29489b -[UINavigationController _startDeferredTransitionIfNeeded:]
14  0x295dc6 _popViewControllerNormal
15  0x296065 -[UINavigationController _popViewControllerWithTransition:allowPoppingLast:]
16  0xe6124b0 -[UINavigationControllerAccessibility(SafeCategory) _popViewControllerWithTransition:allowPoppingLast:]
17  0x2961a8 -[UINavigationController popViewControllerWithTransition:]
18  0x2965b9 -[UINavigationController popToViewController:transition:]
19  0x296257 -[UINavigationController popToRootViewControllerWithTransition:]
20  0x2961de -[UINavigationController popToRootViewControllerAnimated:]
21  0x4868 -[ConnectionViewController getInfo]
22  0x126a6b0 -[NSObject performSelector:withObject:]
23  0x8657 -[MBProgressHUD launchExecution]
24  0xca1805 -[NSThread main]
25  0xca1764 __NSThread__main__
26  0x95108ed9 _pthread_start
27  0x9510c6de thread_start
4

3 に答える 3

1

ほとんどの UIKit フレームワーク メソッドはスレッド セーフではなく、常にメイン スレッドで呼び出す必要があります。あなたの場合、getInfo特にバックグラウンド スレッドから UIKit API を呼び出しています。-[UINavigationController popToRootViewControllerAnimated:]

MBProgressHUDによりgetInfo、バックグラウンド スレッドで呼び出されます。メソッドを参照してくださいshowWhileExecuting:onTarget:withObject:animated:

GCD を使用して、メイン スレッドで UIKit メソッドをディスパッチしてみてください。

-(void)getInfo {    
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    UserPi *newPi = [[UserPi alloc]init];
    newPi.passWord = self.passTextField.text;
    [defaults setObject:self.passTextField.text forKey:@"password"];
    newPi.userName = self.userTextField.text;
    [defaults setObject:self.userTextField.text forKey:@"username"];
    newPi.ipAddress = self.ipTextField.text;
    [defaults setObject:self.ipTextField.text forKey:@"ip"];
    [newPi connectToServer];
    NSString* newAddress = [newPi returnIP];
    self.connected = newPi.connected;     
    // **NOTE**   
    // PS: self.delegate methods should not call UIKit methods
    // if they do, then move them into the main thread callback block
    [self.delegate sendIP:newAddress];
    [self.delegate isConnected:self.connected];
    [defaults synchronize];

    // Do UI work on main thread.
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.navigationController popToRootViewControllerAnimated:YES];    
    });
}
于 2013-06-04T01:25:01.297 に答える
1

-getInfoメインスレッドで呼び出していますか?[self.navigationController popToRootViewControllerAnimated:YES];メインスレッドで呼び出す必要があります。

于 2013-06-04T01:25:18.273 に答える
0

問題は、getInfoメソッドが次のように UI を呼び出すことです。

[self.navigationController popToRootViewControllerAnimated:YES];

…しかし、バックグラウンド スレッドで実行しています。

要点は-showWhileExecuting: onTarget: withObject: animated:、バックグラウンド スレッドでコードを自動的に実行することです。

そのため、バックグラウンド スレッドで手動で実行する場合と同じ方法で保護する必要があります。

したがって、メソッド内のすべての UI コードはperformSelectorOnMainThread:、友人を使用するかdispatch_async、同じことを行う他の手段を使用する必要があります。

この特定のケースでは、プリミティブ ( ) 引数を取るメソッドをディスパッチしたいと考えています。BOOLつまり、単に を使用することはできません-performSelectorOnMainThread: withObject:。しかし、おそらくそれが完了するまで待ちたいと思うでしょう。つまり、単に使用することはできませんdispatch_async

引数を取らないラッパーメソッドを書くことができます:

- (void)popNavigationControllerToRoot {
    [self.navigationController popToRootViewControllerAnimated:YES];
}

… その後:

[self performSelectorOnMainThread:@selector(popNavigationControllerToRoot)
                    waitUntilDone:YES];

NSInvocationまたは、次のようなラッパーを使用できます

[[self.navigationController dd_invokeOnMainThreadAndWaitUntilDone:YES] 
 popToRootViewControllerAnimated:YES];
于 2013-06-04T01:24:32.707 に答える