4

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:

  • Since iOS4, many drawing method of UIKit became thread safe. Read from here.
  • I should not update ui on background thread (e.g. no [myImageView setImage:image];)

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 :)

Community
  • 1
  • 1
Hlung
  • 13,850
  • 6
  • 71
  • 90

3 Answers3

4

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 ....
    }];
}
dlmt
  • 166
  • 1
  • 11
2

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

Arda
  • 757
  • 10
  • 18
1

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.'

Matt
  • 1,586
  • 8
  • 12
  • 1
    @Matt *Q: Is creating UIImage thread safe? A: Yes…* -- do you have a reference for that? – justin Oct 07 '12 at 06:27
  • There is documentation in a comment from this duplicate question: http://stackoverflow.com/questions/10645307/thread-safety-of-uiimage – Matt Oct 07 '12 at 18:56
  • 1
    @Matt the docs you have linked pertain to *drawing* `UIImage`s. however, the referenced docs say nothing about *creation*. as far as i am concerned, jfortmann and CodaFi are the contributors to the linked QA one should pay attention to. there's a lot behind the scenes with images -- that stuff in addition to the object is not necessarily thread safe until the guarantee is published. sure, people do it, but that doesn't mean it's supported -- an OS update or even differences in multithreading workloads (device or workload variance) have the potential to break the program that does this. (cont) – justin Oct 08 '12 at 01:59
  • (cont) it's actually a really good question, and i think proper support is a long time coming. you might think i am paranoid, but i have been using other approaches to load images on secondary threads the whole time because (to my knowledge) that guarantee never existed. – justin Oct 08 '12 at 02:02
  • @justin - Ah, yes it appears that documentation is NOT a sufficient guarantee of thread safety when it comes to loading an image. Maybe its paranoia, but its also better safe than sorry, so taking extra precautions isn't a bad thing. I would just note that jfortmann states that something is either thread safe or it isn't... so (if this is true), while there is nothing in the docs pertaining directly to this issue, and there is not a proper guarantee of safety, the fact that in many people's collective experience no threading issues from loading an image have occurred (AFAIK), we might infer – Matt Oct 08 '12 at 15:44
  • (cont) that the process is fairly safe. I believe a bit more confidence can also be gleaned from the context of the problem itself... how would UIImage be a viable class that doesn't frequently cause main-thread blockage if all images, no matter how expensive to load, must be loaded on the main thread? And, what kind of framework would UIKit be if it didn't offer an appropriate way to load images? I think you raise a good point, that we can't know for sure. But from a practical standpoint, I think it's safe to assume. I'll edit my answer to reflect this. – Matt Oct 08 '12 at 15:51
  • There is no indication that the creation of UIImage is *not* thread safe, or am I missing something? – Eiko Oct 08 '12 at 16:01
  • 1
    @Eiko the default is that UIKit (and AppKit) types should be used from the main thread unless specified differently. – justin Oct 11 '12 at 05:33
  • @justin The creation of UIImages is *the* classic example to put work into the background, i.e. when loading data asynchronously. Creating objects and working with them should be thread safe, up to the point you put them into the view hierarchy. Then, the owner changes to the main thread; from that point on you must be very cautious. – Eiko Oct 11 '12 at 07:10
  • @Eiko we're still lacking a reference. i mentioned that i use other approaches when i load images on secondary threads (e.g. CGImages, which UIImage may wrap). – justin Oct 12 '12 at 21:07
  • According to a WWDC performance session (can't recall which one), an Apple engineer mentioned that creating UIImage objects is fairly lightweight and does not read the entire image into memory at creation time. It only reads enough data from the headers to figure out the type of image and what decoder should be used. The full image is constructed later on. I think this fact makes the thread-safety of UIImage creation and consumption far more interesting, in my opinion. – Anurag Nov 25 '12 at 03:23