我遇到了一个问题,无法解决。如果我的方法签名中有一个带有回调的块参数,并且在我的方法中使用了另一个块的API,则该API会异步执行,而我的代码会继续执行并调用回调方法。这让我的视图控制器知道一切都已经完成,但实际上由于API块的原因往往并不是这样。
由于我的方法总是调用回调方法(并且是异步运行的),我是否可以强制使用块的API调用同步运行? 视图控制器
当我调用[sync]方法时,我的NSLog显示回调方法在任何我的processSync方法完成之前被调用。我认为这是因为processSync方法的调用发生在另一个块内,所以当前线程上的完成块继续被调用。我是否使用块不当,或者有没有典型的处理嵌套块的回调方式。我应该尝试通过一些GCD分派在当前线程上运行二级块吗?建立KVO?我尝试使用KVO遇到的问题是,在sync过程中API(Evernote的)中使用的块数量会有所变化,这取决于发生了什么变化。因此很难确定NSNotificationCenter何时发布通知、同步处于什么阶段以及是否已完成/需要执行回调或发布另一个通知。我假设有一个标准的方法来解决这个问题。任何提示都将不胜感激!Johnathon.
更新1
当我调用[[EvernoteNoteStore noteStore] ^listTagsWithSuccess]方法时,将调用以下代码。
在阅读了Rob的回答后,我发现我需要对源代码进行一些重新架构,这对我来说不是什么大问题。对于每个存在异步代码的方法,方法签名都需要包含一个回调块。异步代码将在完成时调用该回调块。
由于我的方法总是调用回调方法(并且是异步运行的),我是否可以强制使用块的API调用同步运行? 视图控制器
[self.tagRepo sync:^(BOOL finished) {
if (finished) {
NSLog(@"Sync completed.");
if (self.tagRepo.isSyncing)
NSLog(@"Syncing continues...");
}
}];
[标签库同步]
- (void)sync:(void(^)(BOOL finished))completed {
if (self.isSyncing)
return;
NSLog(@"Synchronizing tags...");
self.isSyncing = YES;
[[EvernoteNoteStore noteStore] getSyncStateWithSuccess:^(EDAMSyncState *syncState) {
BOOL performCallback = YES;
if (syncState.fullSyncBefore > lastSyncTime) {
[self processSync:syncState];
} else if (syncState.updateCount > self.lastUpdateCount) {
[self processSync:syncState];
} else {
performCallback = NO; // Block calling the call back when we begin the listTagsWithSuccess async.
self.clientTags = [[self fetchAll] mutableCopy];
[[EvernoteNoteStore noteStore] listTagsWithSuccess:^(NSArray *tags) {
self.serverTags = [tags mutableCopy];
[self processClientTags]; // Only need to make sure client sends any new tags to the server.
// invoke call back.
self.isSyncing = NO;
if (completed)
completed(YES);
} failure:^(NSError *error) {
self.isSyncing = NO;
NSLog(@"Failed to list tags.");
}];
}
self.isSyncing = NO;
if (completed && performCallback)
completed(YES);
} failure:^(NSError *error) {
self.isSyncing = NO;
NSLog(@"Failed to process a sync.");
if (completed)
completed(NO);
}];
}
当我调用[sync]方法时,我的NSLog显示回调方法在任何我的processSync方法完成之前被调用。我认为这是因为processSync方法的调用发生在另一个块内,所以当前线程上的完成块继续被调用。我是否使用块不当,或者有没有典型的处理嵌套块的回调方式。我应该尝试通过一些GCD分派在当前线程上运行二级块吗?建立KVO?我尝试使用KVO遇到的问题是,在sync过程中API(Evernote的)中使用的块数量会有所变化,这取决于发生了什么变化。因此很难确定NSNotificationCenter何时发布通知、同步处于什么阶段以及是否已完成/需要执行回调或发布另一个通知。我假设有一个标准的方法来解决这个问题。任何提示都将不胜感激!Johnathon.
更新1
当我调用[[EvernoteNoteStore noteStore] ^listTagsWithSuccess]方法时,将调用以下代码。
- (void)getSyncStateWithSuccess:(void(^)(EDAMSyncState *syncState))success
failure:(void(^)(NSError *error))failure
{
[self invokeAsyncIdBlock:^id {
return [[self currentNoteStore] getSyncState:[self authenticationToken]];
} success:success failure:failure];
}
- (void)listTagsWithSuccess:(void(^)(NSArray *tags))success
failure:(void(^)(NSError *error))failure
{
[self invokeAsyncIdBlock:^id {
return [[self currentNoteStore] listTags:[self authenticationToken]];
} success:success failure:failure];
}
- (void)invokeAsyncIdBlock:(id(^)())block
success:(void(^)(id))success
failure:(void(^)(NSError *error))failure
{
dispatch_async(self.session.queue, ^(void) {
id retVal = nil;
@try {
if (block) {
retVal = block();
dispatch_async(dispatch_get_main_queue(),
^{
if (success) {
success(retVal);
}
});
}
}
@catch (NSException *exception) {
NSError *error = [self errorFromNSException:exception];
[self processError:failure withError:error];
}
});
}
更新2
我提供了processSync方法来展示其他异步操作的使用。在processSync方法内部,我进行了另一个Evernote SDK方法的调用,最终它会调用processTags方法。我省略了processServerTags,因为代码很大,但包括了processClientTags,它基本上与processServerTags中的所有内容相同。因此,我有3-4个嵌套的Evernote SDK异步块在运行。
- (void)processSync:(EDAMSyncState *)syncState {
BOOL fullSync = NO;
// If we have never updated, perform full sync.
if (!self.lastUpdateCount)
fullSync = YES;
[[EvernoteNoteStore noteStore] getSyncChunkAfterUSN:self.currentChunkUSN maxEntries:200 fullSyncOnly:NO success:^(EDAMSyncChunk *syncChunk) {
// Loop, re-grabbing the next chunk
if (syncChunk.chunkHighUSN < syncChunk.updateCount) {
// Cache the current sync chunk. Since only so much is handed to us
// during this hand-shake, we cache and go get more.
[self cacheSyncChunk:syncChunk];
self.currentChunkUSN = syncChunk.chunkHighUSN;
// Fetch more sync chunks.
[self processSync:syncState];
} else {
// Retrieved the last sync chunk, cache it and begin processing.
[self cacheSyncChunk:syncChunk];
self.currentChunkUSN = syncChunk.chunkHighUSN;
// Build list of server tags
[self processTags];
// Time stamp ourselves so we know when we last updated.
self.lastSyncTime = [NSDate endateFromEDAMTimestamp:syncState.currentTime];
self.lastUpdateCount = syncState.updateCount;
}
} failure:^(NSError *error) {
NSLog(@"Failed to process full sync.");
}];
}
- (void)processTags {
// Process the tags found on the server first. We bring down any new tags from the server and cache them.
// Handles any naming conflicts or duplicate conflicts that come up.
self.clientTags = [[self fetchAll] mutableCopy];
[self processServerTags];
// Process client tags. We check if the client has tags that do not exist on the server and send them.
[self processClientTags];
// Delete any expunged tags that we still have cached.
[self expungeTags];
NSLog(@"Completed syncing tags.");
}
- (void)processClientTags {
NSLog(@"Processing client tags - Ensuring server is current with client tags.");
// Now we compare our local cache to the server, in the event new tags were created.
// TODO: Test this.
for (Tag *clientTag in self.clientTags) {
// Predicate for comparing all client tags against server tags.
// We compare GUID's and Names. Since we can't have duplicate's of either, we check.
// It is possible that the client created a Tag (GUID #1) and created it on the server externally (GUID #2) but share the same name.
// In this case, we have to rename them.
NSPredicate *compareGuidPredicate = [NSPredicate predicateWithFormat:@"guid == %@", clientTag.guid];
//Check if this note exists already on the server.
if (![[self.serverTags filteredArrayUsingPredicate:compareGuidPredicate] count]) {
// If not, we make sure it was never expunged.
if ([self.expungedTags containsObject:clientTag.guid])
continue;
EDAMTag *serverTag = [[EDAMTag alloc] initWithGuid:nil name:clientTag.name parentGuid:nil updateSequenceNum:0];
serverTag = [self convertManagedTag:clientTag toEvernoteTag:serverTag convertingOnlyChangedProperties:NO];
// Check which is newer. If the server is newer, update the client, if the client is newer
// do nothing. It will be handled later under the processClientTags method.
[[EvernoteNoteStore noteStore] createTag:serverTag success:^(EDAMTag *tag) {
NSLog(@"Created new %@ tag on the server.", serverTag.name);
clientTag.guid = tag.guid;
NSLog(@"Server GUID %@ assigned to Client GUID %@", tag.guid, clientTag.guid);
[self saveAllChanges];
} failure:^(NSError *error) {
NSLog(@"Failed to create the %@ tag.\n%@", clientTag.name, [error description]);
}];
}
}
NSLog(@"Client tag processing completed.");
}
在阅读了Rob的回答后,我发现我需要对源代码进行一些重新架构,这对我来说不是什么大问题。对于每个存在异步代码的方法,方法签名都需要包含一个回调块。异步代码将在完成时调用该回调块。