简体   繁体   中英

GCD - bad access on main queue

See this code sample:

dispatch_queue_t downloadQueue=dispatch_queue_create("test", NULL);

        dispatch_async(downloadQueue, ^{

            //do some core data fetching stuff
            //create UIImage            

            //Need to go back to the main thread since this is UI related
            dispatch_async(dispatch_get_main_queue(), ^{

                    if(someView)
                    {
                        [someView addSubview:self];
                    }
                }

            });
        });
        dispatch_release(downloadQueue);

What if "someView" doesn't exist by the time the main thread part of this code runs? Obviously this causes an EXC_BAD_ACCESS crash, but I'm wondering what the best way to handle this situation is. It most often occurs when a user navigates to a page that takes a few seconds to load, but then decides to go back to the previous page while the current page is still loading.

I could imagine some kind of global flag to check before trying to add the subview, but that seems hacky.

dispatch_async(downloadQueue, ^{
    UIView* v = someView;

Now, if at this moment someView has gone out of existence, and if you are using ARC, v is nil and you can test for that.

On the other hand, if someView exists, and if you are using ARC, then v is a strong reference to it, and it cannot go out of existence for the remainder of the block nesting. You could then (at that moment or later in the main thread part of the block) ask about v.window to discover whether the view is still in the interface; if not, it isn't worth proceeding.

// Capture a strong reference to someView, to make sure it's still around later on.
__block UIView *v = someView;

//Need to go back to the main thread since this is UI related
dispatch_async(dispatch_get_main_queue(), ^{
   [v addSubview:self]
   // Release v (assuming ARC). If nothing else has retained someView, this means
   // that someView will be deallocated - but we're on the main thread, 
   // so that's fine. What we don't want is a background dealloc.
   v = nil;
});

OR

// Capture a strong reference to someView, to make sure it's still around later on.
__weak UIView *weakRef = someView;


//Need to go back to the main thread since this is UI related
dispatch_async(dispatch_get_main_queue(), ^{
   // This is thread-safe because someView should only be de-alloced on the main thread.
   // consequently our access to weakRef is safe.
   UIView *strongRef = weakRef;
   [strongRef addSubview:self];
});

What you must not do is this.

   UIView *strongRef = whatever;
    dispatch_async(downloadQueue, ^{
       dispatch_async(dispatch_get_main_queue(), ^{
          [someView addSubview:self]
       });
       // If, by the time we get here, the dispatch to the main queue has already run,
       // then we will send a release to someView on a background thread. If it's the last
       // remaining reference - and it might be, unlikely as it is, then we just dealloced 
       // a UIView in the background. This is bad. 
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM