BluGeni
BluGeni

Reputation: 3454

Threading issue acts differently on ios 7

I am using this Project from github, it is an image picker. I have had to make a very small change since ios7 to make the preview images from your albums show again but with that change now when you leave the picker and come back into it the photos selected (2/5) resets to 0/5 even though I have photos selected. How can I fix this?

The dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) seems to be taking forever to update the ui even with dispatch_async(dispatch_get_main_queue() to reload the ui inside of it. When I comment out the dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) the pictures load instantly but other things get broken that depend on the queue.

here is the code snippet with the dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0) I changed with the code i changed commented out

AGIPCAssetsController.m:

- (void)loadAssets
{
    [self.assets removeAllObjects];

    __ag_weak AGIPCAssetsController *weakSelf = self;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{

        __strong AGIPCAssetsController *strongSelf = weakSelf;

        @autoreleasepool {
            [strongSelf.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {

                if (result == nil) 
                {
                    return;
                }

                AGIPCGridItem *gridItem = [[AGIPCGridItem alloc] initWithImagePickerController:strongSelf.imagePickerController asset:result andDelegate:strongSelf];
                if ( strongSelf.imagePickerController.selection != nil && 
                    [strongSelf.imagePickerController.selection containsObject:result])
                {
                    gridItem.selected = YES;
                }
                [strongSelf.assets addObject:gridItem];
            }];
        }

        dispatch_async(dispatch_get_main_queue(), ^{

            [strongSelf reloadData];

        });

    });
    [strongSelf reloadData];

}

Upvotes: 8

Views: 1574

Answers (4)

Tricertops
Tricertops

Reputation: 8512

AGIPCGridItem is subclass of UIView. Don't work with UIKit objects on background thread.

Make sure you need the background thread and if you do, put only heavy tasks to background. Creating an UIView should not be that case.

Also, it's not recommended to use PRIORITY_LOW use simple PRIORITY_DEFAULT.

Edit: If you are curious why it did work on iOS 6: That's implementation detail of UIKit. It still was wrong, but somehow did what you expected.

Upvotes: 6

Dan Loughney
Dan Loughney

Reputation: 4677

I highlighted iMartin's answer "AGIPCGridItem is subclass of UIView. Don't work with UIKit objects on background thread." He's got it.

I had a very similar issue when moving iOS6 to 7. I was dispatching an ALAssets request in a background thread. Once the fetch completed, I would construct the UIImageView, a UILabel, and a wrapper and then send this object to the main/foreground thread to be rendered. This worked fine on iOS6, but on 7 it was not draw for about 20 seconds. It would sometime draw after a UI event like a touch.

Fix was to fetch the ALAsset in the background, send that to the main thread where I created the image view, etc. Works like a charm now.

Upvotes: 0

quellish
quellish

Reputation: 21244

The global dispatch queue is a shared resource. DISPATCH_QUEUE_PRIORITY_LOW tasks run after every other task in the queue at a higher priority has run. If the queue is getting a lot of blocks submitted with a higher priority, your DISPATCH_QUEUE_PRIORITY_LOW task may not run for a very long time!

This is documented in the Concurreny Programming Guide as well as the libdispatch man pages

So, basically, other higher priority tasks are keeping things busy and your low priority task is not getting an opportunity to go.

Upvotes: 0

vinaut
vinaut

Reputation: 2446

I spent quite a bit of time with this code and I couldn't find a proper solution. Apparently the issue has come up on github, and a user offered a fix:

https://github.com/arturgrigor/AGImagePickerController/issues/19

But apparently he just removed all the blocks running in background, so I suppose that for a large amount of images the performance would be bad.

My hunch is that inside a dispatch_async block runs code that calls some UIKit function, and thus the behaviour is basically undefined.

For example it seems to me that the setAsset function in AGIPGridItem.m is called inside the dispatch_async you posted. It is calling UImage, and although it's inside a lock, it should be still be executed on the background thread, while all the UIKit code should be executed on the main one.

UITableViewCell load images and reused cells

But even if I wrap the call inside a dispatch_async(dispatch_get_main_queue()...) it doesn't work yet.

It seems that the call [view removeFromSuperview]; in setItems in AGIPGridell.m is responsible somehow, but removing it has the side effect of creating a memory leak (unsurprisingly).

Upvotes: 0

Related Questions