I’m trying to enumerate through arrays using the enumerateObjectsWithOptions:usingBlock method. However, my code rarely works. When it does not work, my app freezes (but no beach ball) — I’m relatively new to blocks, so I must be missing something that is required for block operations to work smoothly.
Note: I know I can enumerate using for loops, but this is not what I am looking for.
The troublesome code:
NSArray*_storageCookies = [[NSArray alloc] initWithArray:[storage cookies]];
NSArray*_historyObjects = [[NSArray alloc] initWithArray:[_history webkitHistory]];
NSOperationQueue*_queue = [[NSOperationQueue alloc] init];
NSBlockOperation*_block = [NSBlockOperation blockOperationWithBlock:^{
NSAutoreleasePool*_pool = [[NSAutoreleasePool alloc] init];
[_storageCookies enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id ck, NSUInteger index, BOOL *stop)
{
NSString*domain = [ck domain];
[_historyObjects enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id object, NSUInteger aindex, BOOL *stop)
{
@synchronized(self)
{
NSAutoreleasePool*_pool2 = [[NSAutoreleasePool alloc] init];
NSString*_historyURL = [[NSURL URLWithString:[object url]] host];
if ([_historyURL rangeOfString:domain].location != NSNotFound)
{
NSHTTPCookie*cookie = [DAHTTPCookie createCookieWithURL:[ck domain] cookieName:[ck name] expires:[[ck expiresDate] timeIntervalSince1970] cookieValue:[ck value] browserType:DAWebkitBrowser secure:[ck isSecure]];
if ([_cookies containsObject:cookie] == NO)
{
[_cookies addObject:cookie];
}
}
[_pool2 release];
}
}];
}];
[_pool release];
}];
[_queue addOperations:[NSArray arrayWithObject:_block] waitUntilFinished:YES];
NSLog(@"Done - found %i Cookies...",[_cookies count]); //sometimes returns 0, sometimes the right number or nothing
Edit
I have fixed it. You were right, my class was not thread safe. So I had to add an @synchronized block for this to work as expected.
I don’t see where _cookies is defined, but I guess we can assume it is an NSMutableArray. Those are not threadsafe, so if you have multiple threads reading and also adding objects to that array at the same time, that is a problem.
There are different ways to solve that. One option is to create a serial queue that owns that array. In the course of your processing, each time you need to check for (and if needed add) a cookie, schedule a small block on that queue to do that work. That way you can ensure those reads and adds are strictly serial.
There are other ideas for this also of course.
But then taking a step back, once you introduce that dance to keep the threads from stepping all over those _cookies, you may or may not find this is the most efficient implementation. You’ll need to test that with your actual data.
Also, why the NSOperation here? Since you wait for the results anyway, why not just kick off the enumeration on the main queue and then let it fan out from there? (I may be missing something)