UNIRest:在异步 HTTP 响应上切换到新的 ViewController

UNIRest: switch to a new ViewController on an Asynchronous HTTP response

我正在使用 UNIRest library,我正在尝试根据 HTTP GET method 的响应切换到另一个 ViewController

[[UNIRest get:^(UNISimpleRequest *request) {
        [request setUrl:GEtFamilyMembers];
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) {
        NSLog(@"res: %@", response.body);
        if (response.code == 200) {
            GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];

            self.navigationController.viewControllers = @[fmViewController];
            [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];
        } else {
            [self showToast:@"Something went wrong, Please try again after some time."];
        }
    }];

即使在使用 NSLog 打印响应之后。大约 30 秒内没有任何反应。然后突然应用程序崩溃。

我认为这是因为异步调用阻塞 UI 所以我厌倦了这样的方法:

- (void) gotoSelectFamilyMembers {
    dispatch_async(dispatch_get_main_queue(), ^{
        GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];
        self.navigationController.viewControllers = @[fmViewController];
        [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];
    });
}

但应用程序仍然崩溃。

早些时候,当我使用 Synchronous 调用时,它是有效的。但是不是现在。 请帮忙!

编辑:

2016-05-02 17:58:22.695 Checkme Mobile[6208:136321] *** Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /SourceCache/UIKit_Sim/UIKit-3347.44.2/Keyboard/UIKeyboardTaskQueue.m:374
2016-05-02 17:58:22.728 Checkme Mobile[6208:136224] NSScanner: nil string argument
2016-05-02 17:58:22.728 Checkme Mobile[6208:136224] NSScanner: nil string argument
2016-05-02 17:58:23.730 Checkme Mobile[6208:136224] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001094a6c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000108da2bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x00000001094a6aca +[NSException raise:format:arguments:] + 106
    3   Foundation                          0x00000001089b798f -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
    4   UIKit                               0x0000000107d887d6 -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] + 151
    5   UIKit                               0x0000000107829912 -[UIKeyboardImpl setDelegate:force:] + 473
    6   UIKit                               0x0000000107ad44ad -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:] + 1002
    7   UIKit                               0x0000000107adc834 -[UIPeripheralHost(UIKitInternal) _preserveInputViewsWithId:animated:reset:] + 504
    8   UIKit                               0x000000010778e181 -[UINavigationController navigationTransitionView:didStartTransition:] + 578
    9   UIKit                               0x00000001079948bc -[UINavigationTransitionView transition:fromView:toView:] + 655
    10  UIKit                               0x0000000107792170 -[UINavigationController _startTransition:fromViewController:toViewController:] + 2984
    11  UIKit                               0x0000000107792408 -[UINavigationController _startDeferredTransitionIfNeeded:] + 523
    12  UIKit                               0x0000000107792ece -[UINavigationController __viewWillLayoutSubviews] + 43
    13  UIKit                               0x00000001078dd6d5 -[UILayoutContainerView layoutSubviews] + 202
    14  UIKit                               0x00000001076b09eb -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 536
    15  QuartzCore                          0x0000000109d83ed2 -[CALayer layoutSublayers] + 146
    16  QuartzCore                          0x0000000109d786e6 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    17  QuartzCore                          0x0000000109d78556 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    18  QuartzCore                          0x0000000109ce486e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
    19  QuartzCore                          0x0000000109ce5a22 _ZN2CA11Transaction6commitEv + 462
    20  QuartzCore                          0x0000000109ce5c99 _ZN2CA11Transaction14release_threadEPv + 199
    21  libsystem_pthread.dylib             0x000000010a35172a _pthread_tsd_cleanup + 86
    22  libsystem_pthread.dylib             0x000000010a351451 _pthread_exit + 117
    23  libsystem_pthread.dylib             0x000000010a3506cd _pthread_wqthread + 879
    24  libsystem_pthread.dylib             0x000000010a34e40d start_wqthread + 13
)
2016-05-02 17:58:23.730 Checkme Mobile[6208:136321] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001094a6c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000108da2bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x00000001094a6aca +[NSException raise:format:arguments:] + 106
    3   Foundation                          0x00000001089b798f -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
    4   UIKit                               0x0000000107d887d6 -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] + 151
    5   UIKit                               0x0000000107829912 -[UIKeyboardImpl setDelegate:force:] + 473
    6   UIKit                               0x0000000107ad44ad -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:] + 1002
libc++abi.dylib:    7   UIKit                               0x0000000107adc834 -[UIPeripheralHost(UIKitInternal) _preserveInputViewsWithId:animated:reset:] + 504
    8   UIKit                               0x000000010778e181 -[UINavigationController navigationTransitionView:didStartTransition:] + 578
    9   UIKit                               0x00000001079948bc -[UINavigationTransitionView transition:fromView:toView:] + 655
    10  UIKit                               0x0000000107792170 -[UINavigationController _startTransition:fromViewController:toViewController:] + 2984
    11  UIKit                               0x0000000107792408 -[UINavigationController _startDeferredTransitionIfNeeded:] + 523
    12  UIKit                               0x0000000107792ece -[UINavigationController __viewWillLayoutSubviews] + 43
    13  UIKit                               0x00000001078dd6d5 -[UILayoutContainerView layoutSubviews] + 202
    14  UIKit                               0x00000001076b09eb -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 536
    15  QuartzCore                          0x0000000109d83ed2 -[CALayer layoutSublayers] + 146
    16  QuartzCore                          0x0000000109d786e6 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    17  QuartzCore                          0x0000000109d78556 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    18  QuartzCore                          0x0000000109ce486e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
    19  QuartzCore                          0x0000000109ce5a22 _ZN2CA11Transaction6commitEv + 462
    20  QuartzCore                          0x0000000109ce5c99 _ZN2CA11Transaction14release_threadEPv + 199
    21  libsystem_pthread.dylib             0x000000010a35172a _pthread_tsd_cleanup + 86
    22  libsystem_pthread.dylib             0x000000010a351451 _pthread_exit + 117
    23  libsystem_pthread.dylib             0x000000010a3506cd _pthread_wqthread + 879
    24  libsystem_pthread.dylib             0x000000010a34e40d start_wqthread + 13
)
terminating with uncaught exception of type NSExceptionlibc++abi.dylib: 
terminating with uncaught exception of type NSException
(lldb) 

编辑:

- (IBAction)loginAction:(id)sender {
    self.email = usernameTextField.text;
    self.password = passwordTextField.text;
    if(!([self.email isEqualToString:@""] && [self.password isEqualToString:@""])) {
        [self performLogin];
    } else{
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"error" message:@"Enter both username and password." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
        [alert show];
    }
}

- (void) performLogin {
    NSDictionary* headers = @{@"Content-Type": @"application/x-www-form-urlencoded"};
    NSDictionary* parameters = @{@"username": self.email, @"password": self.password};
     [[NSThread currentThread] isMainThread] ? NSLog(@"1MAIN THREAD") : NSLog(@"1NOT MAIN THREAD");
    self.loginConnection = [[UNIRest post:^(UNISimpleRequest *request) {
        [request setUrl:LOgin];
        [request setHeaders:headers];
        [request setParameters:parameters];
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) {
        // This is the asyncronous callback block
         [[NSThread currentThread] isMainThread] ? NSLog(@"2MAIN THREAD") : NSLog(@"2NOT MAIN THREAD");
        UNIJsonNode *body = response.body;
        NSData *rawBody = response.rawBody;
        NSString *string = [[NSString alloc] initWithData:rawBody encoding:NSUTF8StringEncoding];
        NSLog(@"response signup: %@", string);
        if (response.code == 200) {
            [self processLoginResponse:body];
        }
    }];

}


- (void) processLoginResponse:(UNIJsonNode *) body {
    if ([body.JSONObject[@"status"] boolValue] == YES) {
        NSString *firstName = body.JSONObject[@"firstName"];
        NSString *lastName = body.JSONObject[@"lastName"];
        NSString *name = [firstName stringByAppendingString:lastName];
        NSMutableDictionary *mDictionary = [[NSMutableDictionary alloc] init];
        [mDictionary setObject:body.JSONObject[@"accountId"] forKey:@"accountId"];
        [mDictionary setObject:name forKey:@"name"];
        [mDictionary setObject:@"Custodian" forKey:@"relation"];
        [self fetchFamilyMembers:mDictionary];
    } else {
        [self showAlert:body.JSONObject[@"message"]];
    }
}

- (void) fetchFamilyMembers:(NSMutableDictionary *) mDictionary {
     [[NSThread currentThread] isMainThread] ? NSLog(@"3MAIN THREAD") : NSLog(@"3NOT MAIN THREAD");
    self.fmConnection = [[UNIRest get:^(UNISimpleRequest *request) {
        [request setUrl:GEtFamilyMembers];
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) {
        NSLog(@"res: %@", response.body);
        if (response.code == 200) {
             [[NSThread currentThread] isMainThread] ? NSLog(@"4MAIN THREAD") : NSLog(@"4NOT MAIN THREAD");
            NSData *rawBody = response.rawBody;
            NSString *string = [[NSString alloc] initWithData:rawBody encoding:NSUTF8StringEncoding];
            NSLog(@"family signup: %@", string);
            [[NSThread currentThread] isMainThread] ? NSLog(@"5MAIN THREAD") : NSLog(@"5NOT MAIN THREAD");
            [self processFamilyMembersResponse:mDictionary :response.body];

        } else {
            [self showToast:@"Something went wrong, Please try again after some time."];
        }
    }];
}

- (void) processFamilyMembersResponse:(NSMutableDictionary *) mDictionary :(UNIJsonNode *) body {

    NSArray *fmArray = body.JSONArray;
    if (fmArray == nil || [fmArray count] == 0) {
        NSNumber *accountId = (NSNumber *)[mDictionary objectForKey:@"accountId"];

    } else {
        NSMutableArray *mArray = [[NSMutableArray alloc] init];
        [mArray addObject:mDictionary];
        for (int i = 0; i < fmArray.count; i++) {
            NSDictionary *fmDictionary = [fmArray objectAtIndex:i];
            mDictionary = [[NSMutableDictionary alloc] init];
            NSNumber *accountId = (NSNumber *)[fmDictionary objectForKey:@"id"];
            [mDictionary setObject:accountId forKey:@"accountId"];
            [mDictionary setObject:[fmDictionary objectForKey:@"name"] forKey:@"name"];
            [mDictionary setObject:[fmDictionary objectForKey:@"relation"] forKey:@"relation"];
            [mArray addObject:mDictionary];
        }
        [[NSThread currentThread] isMainThread] ? NSLog(@"6MAIN THREAD") : NSLog(@"6NOT MAIN THREAD");
        [self gotoSelectFamilyMembers:mArray];
    }

}

- (void) gotoSelectFamilyMembers:(NSMutableArray *) mArray {
    dispatch_async(dispatch_get_main_queue(), ^{
        GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];
        fmViewController._tblData = mArray;
        self.navigationController.viewControllers = @[fmViewController];
        [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];
    });
}

您正在使用 Unirest 的异步调用,应该在 UI thread (Main thread) 上调用响应。通常与此类似的库会在主线程上传递结果。因此,我认为您不需要在 main queue 上专门调用 dispatch_async。但是,您可以使用以下检查来查看它是否在主线程上被调用。

 [[NSThread currentThread] isMainThread] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

这是我的怀疑

  1. 您的ViewController创建方法有问题并且耗时
  2. 你的回复很大

我建议您在不调用任何网络服务的情况下调用以下内容,以确认它们不会导致崩溃。

GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];

            self.navigationController.viewControllers = @[fmViewController];
            [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];

试试下面的代码:

dispatch_async(dispatch_get_main_queue(), ^{
        GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];
        fmViewController._tblData = mArray;

        ((AppDelegate*)[[UIApplication sharedApplication]delegate]).navigationController.viewControllers = @[fmViewController];
        [self.mm_drawerController setCenterViewController:((AppDelegate*)[[UIApplication sharedApplication]delegate]).navigationController withCloseAnimation:YES completion:nil];
    });