CKAccountStatus 在未从 ViewController 调用时返回不同的值

CKAccountStatus returning different values when not called from ViewController

我在我的应用程序中使用 Cloud Kit,它使用相机并且只允许用户在登录到 iCloud 时提交照片。因此,当用户单击相机按钮时,我调用了一个 CloudKit 方法来获取用户的 icloud 状态,其中 return 是一个 CKAccountStatus 值 (0-3)。我最初在视图控制器中实现了它并且它工作得很好。然后我做了一些重构并创建了一个 CKManager class 来容纳所有与 CK 相关的方法。所以现在当单击相机而不是直接在 VC 中调用容器外的 CK 方法时,我通过我的 CKManager 属性 中的方法调用它(这是惰性实例化的)。它应该只 return 值 0-3,但由于某种原因它保持 returning 448。但是,在 CKManager 日志记录中,我可以看到它正确记录了我已登录到 iCloud。所以它从那里转换回 VC 时存在问题。我觉得这是一个 threading/callback 的问题,我不是很精通。

谁能看一下代码,看看是否有明显的我做错了什么?提前致谢!

- (IBAction)cameraBarButtonPressed:(UIBarButtonItem *)sender {
NSLog(@"Entered cameraBarButtonPressed");

//CKContainer *container = [CKContainer defaultContainer];
dispatch_queue_t fetchQ = dispatch_queue_create("check user status", NULL);
__block CKAccountStatus userAccountStatus;

dispatch_async(fetchQ, ^{ // check user's CK status on different thread
    userAccountStatus = [self.ckManager getUsersCKStatus];
    NSLog(@"cameraBarButtonPressed userAccountStatus: %ld", userAccountStatus);

    if (userAccountStatus == CKAccountStatusAvailable) {
        //NSLog(@"User is logged into CK - user can upload pics!");
        UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
        cameraUI.delegate = self; // set the deleage for the ImagePickerController

        // check to see if the camera is available as source type, else check for photo album
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
            cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
        } else if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {
            cameraUI.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
        }

        [cameraUI setAllowsEditing:YES]; // let the user edit the photo
        // set the camera presentation style
        //cameraUI.modalPresentationStyle = UIModalPresentationFullScreen;
        cameraUI.modalPresentationStyle = UIModalPresentationCurrentContext;

        dispatch_async(dispatch_get_main_queue(), ^{ // show the camera on main thread to avoid latency
            [self presentViewController:cameraUI animated:YES completion:nil]; // show the camera with animation
        });
    } else if (userAccountStatus == CKAccountStatusNoAccount) {
        //NSLog(@"User is not logged into CK - Camera not available!");
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"iCloud Not Available" message:@"You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        dispatch_async(dispatch_get_main_queue(), ^{
            [alert show];
        });
    } else if (userAccountStatus == CKAccountStatusRestricted) {
        NSLog(@"User CK account is RESTRICTED !");
    } else if (userAccountStatus == CKAccountStatusCouldNotDetermine) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"iCloud Status Undetermined" message:@"We could not determine your iCloud status. You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        dispatch_async(dispatch_get_main_queue(), ^{
            [alert show];
        });
    } else { // did not get back one of the above values so show the Could Not Determine message
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"iCloud Status Undetermined" message:@"We could not determine your iCloud status. You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        dispatch_async(dispatch_get_main_queue(), ^{
            [alert show];
        });

    }
});
}

以上代码是无效的代码。这是有效的代码。只需复制开头的代码,因为从那时起其余的代码都是多余的...

CKContainer *container = [CKContainer defaultContainer];
dispatch_async(fetchQ, ^{ // check user's CK status on different thread
    [container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
        if (error) {...

最后,这里是从 CKManager 调用的代码,因为代码不起作用...

- (CKAccountStatus)getUsersCKStatus {

NSLog(@"Entered getUsersCKStatus...");
__block CKAccountStatus userAccountStatus;

[self.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
    if (error) {
        NSLog(@"Error: Error encountered while getting user CloudKit status: %@", error.localizedDescription);
    } else {
        if (accountStatus == CKAccountStatusAvailable) {
            NSLog(@"Info: User is logged into CK - camera is available!");
            userAccountStatus = CKAccountStatusAvailable;
        } else if (accountStatus == CKAccountStatusNoAccount) {
            NSLog(@"Info: User is not logged into CK - Camera not available!");
            userAccountStatus = CKAccountStatusNoAccount;
        } else if (accountStatus == CKAccountStatusRestricted) {
            NSLog(@"Info: User CK account is RESTRICTED - what does that mean!?");
            userAccountStatus = CKAccountStatusRestricted;
        } else if (accountStatus == CKAccountStatusCouldNotDetermine) {
            NSLog(@"Error: Could not determine user CK Account Status: %@", error.localizedDescription);
            userAccountStatus = CKAccountStatusCouldNotDetermine;
        }
    }
}];

NSLog(@"CKAccountStatus: %ld", userAccountStatus);

return userAccountStatus;

}

在 getUsersCKStatus 中,您正在调用 accountStatusWithCompletionHandler。那是一个异步方法。在您的情况下,它将 return userAccountStatus 在其回调方法设置之前。

您可以通过实现信号量使该方法同步来解决此问题。更好的方法是将回调块传递给该方法,而不是 returning 一个值。