Hlung
Hlung

Reputation: 14298

Is creating UIImage thread safe?

Is calling something like this considered thread safe ? It's only creating a UIImage, no UI updating. I can't find any documentation about this.

UIImage * hiResImage = [[UIImage alloc] initWithContentsOfFile:path]; 

FYI, I later do the UI update on main thread like this...

[imageViewForZoom performSelectorOnMainThread:@selector(setImage:) withObject:hiResImage waitUntilDone:NO];

What I already know:

EDIT: Let's look at another point of view. Does 'not-thread-safe' means that there is a chance that it can be blocked forever? or just means there is no guarantee on the start/duration execution time. If it is the latter case, then there is no problem if we would have some 'undetermined amount' of delay when loading an image. The UI update is done on main thread. So, it is still considered OK, for creating UIImage at least, to be not-thread-safe.

I know this is not really related to the question but just want to point it out since I fear there will be no clear-cut answer to my original question :)

Upvotes: 4

Views: 3397

Answers (3)

Arda
Arda

Reputation: 902

According to Apple, the answer is yes, it is safe to create a UIImage from any thread:

Because image objects are immutable, you cannot change their properties after creation. Most image properties are set automatically using metadata in the accompanying image file or image data. The immutable nature of image objects also means that they are safe to create and use from any thread.

https://developer.apple.com/reference/uikit/uiimage

Upvotes: 2

dlmt
dlmt

Reputation: 166

My experience below is not directly with UIImage initContentsFromFile: but with UIImage imageWithData, but your question is about UIImage thread safety.

I recently had to debug an issue with using [UIImage imageWithData:] that is called from a NSURLConnetionDelegate function connectionDidFinishLoading to download images using several background threads. Since the downloaded images are used for updating the UI, I had to use [NSOperationQueue mainQueue] addOperationWithBlock ... below:

- (void) connection:(URLConnection*)connection didReceiveData:(NSData *) data {
    [imgData appendData:data];
}

- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
    [NSOperationQueue mainQueue] addOperationWithBlock:^{
        UIImage *img = [UIImage imageWithData:imgData];
        // more code here to update the UI
    }];
}
  • When running on an iOS 7.x simulator, img holds a valid image
  • When running on an iOS 7.x device (iPod Touch), img is always nil

During a debugging session, I noticed that the issue (temporarily) disappeared when the debugger stepped through each statement one line at a time. My theory is that running in debugger step-mode does not put UIImage to handle concurrent threads running imageWithData. Therefore, I believe that UIImage imageWithData (and perhaps other similar functions) are not thread-safe.

Using a @synchronized block seems to solve the issue

- (void) connectionDidFinishLoading:(NSURLConnection*)connection {
    [NSOperationQueue mainQueue] addOperationWithBlock:^{
        @synchronized(imgData) {
            // Run the following in a synchronized block
            UIImage *img = [UIImage imageWithData:imgData];
        }
        // more code here ....
    }];
}

Upvotes: 4

Matt
Matt

Reputation: 1586

Yes. It is fairly common practice to load images in the background, mainly if its a remote file, or if many images are being loaded. And yes, only update UI on the main thread.

EDIT:

Due to some enlightening comments, I would revise my first answer of 'Yes' to 'Based on experience and my assessment of what wouldn't be a viable alternative for UIImage's thread safety when it comes to loading an image, I think it's reasonable to assume it is. However, each person is warranted their own opinion, and perhaps his or her risk associated with code failure here is too high to make assumptions under any circumstances.'

Upvotes: 1

Related Questions