I have a simple class that exposes a method that makes multiple server calls to fetch object data for a list of object IDs. That method enumerates through the list of object IDs and makes individual server calls, shown below...
@implementation MyClass
- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {
typeof(self) __weak blockSelf = self;
for(NSString *objectID in objectIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if(objectID == [objectIDs lastObject]) { //<---will this work?
[blockSelf.delegate finishedFetching];
}
}];
}
}
- (void)fetchObjectDataForObjectID:(NSString*)objectID
withSuccessBlock:(void (^)())successBlock {
void (^successWrapperBlock)(void) = ^{
//Some processing of returned data is done here
if(successBlock) {
successBlock();
}
};
[HTTPClient fetchObjectDataWithObjectID:objectID
withSuccessBlock:successWrapperBlock
failureBlock:nil];
}
@end
...I'm trying to figure out the best way to check if the last successBlock
is being executed so I can tell the delegate it's finished fetching the data. I have a comment in the code "<---Will this work?" marking the statement that's doing the check, but it doesn't feel safe to me since the block is asynchronous and if(objectID == [objectIDs lastObject])
could be true when the first successBock if executed.
How can I determine when the last successBlock
is being executed? Thanks in advance for your wisdom!
The pointer comparison (objectID == [objectIDs lastObject]
) is alright here as you will get the same objects if the array is not modified. If you are concerned that objectIDs
might get modified (e.g. that it might actually be a mutable array and get modified on another thread or through side effects like notifications along the way), the best protection would be to copy the array right at the beginning:
- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {
typeof(self) __weak blockSelf = self;
NSArray *copiedIDs = [objectIDs copy];
for(NSString *objectID in copiedIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if(objectID == [copiedIDs lastObject]) {
[blockSelf.delegate finishedFetching];
}
}];
}
}
There are now two ways to call the delegate:
So you can:
So you could do (@ChrisH's version plus copy
):
- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {
typeof(self) __weak blockSelf = self;
NSArray *copiedIDs = [objectIDs copy];
__block NSUInteger count = [copiedIDs count];
for(NSString *objectID in copiedIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if(--count == 0) {
[blockSelf.delegate finishedFetching];
}
}];
}
}
or:
- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {
typeof(self) __weak blockSelf = self;
NSArray *copiedIDs = [objectIDs copy];
id lastObject = [copiedID lastObject];
for(NSString *objectID in copiedIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if(objectID == lastObject) {
[blockSelf.delegate finishedFetching];
}
}];
}
}
This is how I've handled the same situation in the past:
- (void)fetchObjectsFromServerWithObjectIDs:(NSArray*)objectIDs {
typeof(self) __weak blockSelf = self;
__block int c = [objectIDs count];
for (NSString *objectID in objectIDs) {
[self fetchObjectDataForObjectID:objectID withSuccessBlock:^{
if (--c == 0) {
[blockSelf.delegate finishedFetching];
}
}];
}
}
It assumes that the array isn't going to be modified during iteration, which I would infer in this case as the object being passed is an immutable array.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With