Gcd - Main VS Background Thread for Updating a Uiimageview

GCD - main vs background thread for updating a UIImageView

Yes, you need to use the main thread whenever you're touching UIImageView, or any other UIKit class (unless otherwise noted, such as when constructing UIImages on background threads).

One commentary about your current code: you need to assign weakSelf into a strong local variable before using it. Otherwise your conditional could pass, but then weakSelf could be nilled out before you actually try to use it. It would look something like

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *tmpImage = [[UIImage alloc] initWithCGImage:asset.defaultRepresentation.fullScreenImage];

__strong __typeof__(weakSelf) strongSelf = weakSelf;
if ([strongSelf.scrollView viewWithTag:tagNumber] != nil){

dispatch_async(dispatch_get_main_queue(), ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
if ([strongSelf.scrollView viewWithTag:tagNumber]!= nil){
UIImageView * tmpImageView = (UIImageView*)[strongSelf.scrollView viewWithTag:tagNumber];
tmpImageView.image = tmpImage;
}
});
}
});

Technically you don't need to do this in the first conditional in the background queue, because you're only dereferencing it once there, but it's always a good idea to store your weak variable into a strong variable before touching it as a matter of course.

How to download and set a UIImage not on the main thread

You can use the GCD to download image like this.

//Your ImageView
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 10, 300, 180)];
// Create a __block type reference for the variable, for ARC use __weak instead
__block UIImageView *blockimage = iv;

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *imageData=[NSData dataWithContentsOfURL:[NSURL URLWithString:mainImageUrl]];
dispatch_sync(dispatch_get_main_queue(), ^{
blockimage.image = [UIImage imageWithData:imageData];
// To avoid retain cycle
blockimage = nil;
});
});

This code is downloading the image synchronously within GCD async block by using dataWithContentsOfURL: API, disadvantage is you cannot get the precise information when image fails to download. In this case to properly handle error situations, use NSURLConnection to asynchronously download the image data.

Also, as suggested correctly in few of answers to your question, you can use SDWebImage or AFNetworking image view categories. This will give you image cache facility and ensure that the image is downloaded asynchronously without affecting the main thread performance.

Hope that helps!

main.async vs main.sync() vs global().async in Swift3 GCD

In simple term i come to conclusion that -

  • Queue- There are 3 Types of Queue i.e. 1 Main Queue, 4 Global Queue and Any No. of Custom Queues.
  • Threads- One is Main Thread and other background threads which system
    provides to us.

DispatchQueue.main.async

-It means performing task in main queue with using of background thread (w/o blocking of UI) and when task finish it automatic Updated to UI because its already in Main Queue.

DispatchQueue.global().async along with global().sync

It means performing task in Global Queue with using of background thread and when task finish, than global().sync use bring the work from globalQueue to mainQueue which update to UI.

Reason of My App Crash

I was trying to bring the completed task to MainQueue by using(main.sync), but it was already on MainQueue because i hadnt switched the Queue, and this create DeadLock (MainQueue waiting for itself), causes my app crash

Update UI on dispatch_get_main_queue()

This is a good question. And the answer when using ARC is essentially that the block itself retains the object, so that it will be around later. This is one of those subtle memory gotchas. If the UITableView from which this cell comes is de-allocated and releases all of its cells, this one will be retained (though off-screen) and will have the assignment of cell.imageView = image; completed, then it will be released.

I am a big fan of controlled experiments and set out to test this, but a UITableView has many moving parts (no pun intended). So I created a very simple experiment, using a simple NSObject subclass as follows:

@implementation SayHello
-(void)sayHello{
NSLog(@"Hello");
}
-(void)dealloc{
NSLog(@"SayHello dead");
}
@end

Obviously this class is intended to give me a function to call in a block (sayHello) and will produce an NSLog when de-allocated.

I ran my test like this:

SayHello *hello = [[SayHello alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
double delayInSeconds = 30.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[hello sayHello];
});
});

The 30 seconds gives even the most lazy runloop time to deallocate the "hello" object (if, in fact, it was not retained). But for those 30 seconds the console is silent. After the 30 seconds have expired I get the "Hello" immediately followed by the "SayHello dead" message.

So how is this a "gotcha"? Well, obviously if you don't realize that Blocks/ARC are doing this, it could end up keeping around things you thought should be gone. But also with your example of a UITableViewCell; what if your cell is displayed once and sends a request over the network for an image, but while the block is waiting for the image the cell is reused? Now there is a second block with reference to that cell trying to set its image. Well, now you have a race in which the loser will decide what image is displayed.

Block_release deallocating UI objects on a background thread

You can use the __block storage type qualifier for such a case. __block variables are not automatically retained by the block. So you need to retain the object by yourself:

__block UIViewController *viewController = [myViewController retain];
dispatch_async(backgroundQueue, ^{
// Do long-running work here.
dispatch_async(dispatch_get_main_queue(), ^{
[viewController updateUIWithResults:results];
[viewController release]; // Ensure it's released on main thread
}
});

EDIT

With ARC, __block variable object is automatically retained by the block, but we can set nil value to the __block variable for releasing the retained object whenever we want.

__block UIViewController *viewController = myViewController;
dispatch_async(backgroundQueue, ^{
// Do long-running work here.
dispatch_async(dispatch_get_main_queue(), ^{
[viewController updateUIWithResults:results];
viewController = nil; // Ensure it's released on main thread
}
});

Is UIImageJPEGRepresentation() thread safe?

You can use UIImageJPEGRepresentation() in the background (I'm using it this way in a current project).

However what you can't do is create a UIImage the way you are doing in the background, the [UIImage imagewithCGImage] call must be doing in the main thread (as a rule of thumb all UIKit calls should be done on the main thread).

This seems like a case where you might need nested blocks.

Edit: My own code I have found does call [UIImage imagewithCGImage] while in a background thread, but I am still suspicious that might cause issues in some cases. But my code does work.

Edit2: I just noticed you are resizing the image, UIImage+Resize. There's a very nice class linked to in this post, that has been built to do that in a robust way:

http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

You should really read that whole page to understand the nuances of resizing images. As I said, I do use that from a background thread even though part of what it does inside is what you were doing.

Edit3: If you are running on iOS4 or later, you may want to look into using the ImageIO framework to output images, which is more likely to be thread safe:

http://developer.apple.com/graphicsimaging/workingwithimageio.html

Example code for that is hard to find, here's a method that saves a PNG image using ImageIO (based on the code in "Programming With Quartz:2D and PDF graphics in Mac OS X):

// You'll need both ImageIO and MobileCoreServices frameworks to have this compile
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>

void exportCGImageToPNGFileWithDestination( CGImageRef image, CFURLRef url)
{
float resolution = 144;
CFTypeRef keys[2];
CFTypeRef values[2];
CFDictionaryRef options = NULL;

// Create image destination to go into URL, using PNG
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithURL( url, kUTTypePNG, 1, NULL);

if ( imageDestination == NULL )
{
fprintf( stderr, "Error creating image destination\n");
return;
}

// Set the keys to be the X and Y resolution of the image
keys[0] = kCGImagePropertyDPIWidth;
keys[1] = kCGImagePropertyDPIHeight;

// Create a number for the DPI value for the image
values[0] = CFNumberCreate( NULL, kCFNumberFloatType, &resolution );
values[1] = values[0];

// Options dictionary for output
options = CFDictionaryCreate(NULL,
(const void **)keys,
(const void **)values,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);

CFRelease(values[0]);

// Adding the image to the destination
CGImageDestinationAddImage( imageDestination, image, options );
CFRelease( options );

// Finalizing writes out the image to the destination
CGImageDestinationFinalize( imageDestination );
CFRelease( imageDestination );
}


Related Topics



Leave a reply



Submit