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 一个值。
我在我的应用程序中使用 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 一个值。