Best multithreaded approach in Objective-C?

I am developing an iPad application, and now I am struggling to find the best approach to multithreading. Let me illustrate this with a simplified example:
I have a view with 2 subviews, a catalog picker and a gallery with thumbnails of all the images in the selected directory. Since loading and creating these thumbnails can take quite a while, I need multithreading, so interaction and updating the view are not blocked.

This is what I have already tried:
[self performSelectorInBackground: @selector (displayThumbnails :) withObject: currentFolder];
This worked fine, because user interaction was not blocked, but it fails when the user enters another folder while the first folder is still loading. Two threads try to access the same view and variables, which leads to incorrect execution of each of them. When users select another folder, the displayThumbnails download folder should currently be interrupted. I did not find a way to do this.

Nsthreads
I tried this, but struggled with almost the same problems as with the first method, I did not find a (easy) way to cancel the current method. (Yes, I know about [aThread cancel] , but did not find a way to "resume" the thread). Maybe I need a subclass of NSThread and implement my own isRunning methods, etc.? But is there a better way or a third (or even fourth and fifth) option that I miss?

I think this is a fairly simple example, and I think a better solution is possible without a subclass of NSThread . So what would you do? Your opinion please!

+6
source share
3 answers

NSOperationQueue should work well for this task.

Another option would be a simple GCD , however, if you've never worked with it, NSOperationQueue is probably the best choice, since it pretty much automatically leads you to implement things β€œcorrectly”, has obvious ways to cancel, etc.

+6
source

You want to use Concurrent NSOperations to download and process images in the background. They will be managed by NSOperationsQueue. In essence, these operations will be configured to receive one image per operation, process it, save it in the file system, then message back to the main application of the main stream that the image is available.

There are several projects on github where you can see how to do this - just find github using "Concurrent" or "NSOperation".

iOS has a really good tool for doing background work. Grand Central Dispatch (GCD) and Blocks, but this does not allow you to have an object using delegate callbacks - thus NSOperation.

So, you need to read on the blocks, GCD, and then take a look at some open source Concurrent NSOperations code. Using Concurrent NSOperations is not as simple as using blocks.

+5
source

If I had this problem, I would probably go for this approach:

  • one thread that will load images and make the main thread display the results (I'm not a big fan of creating threads with GUI objects)

  • when a new directory is requested ... well, it depends on how you want to manage things. In principle, the standard queue design (condition variable and array) can be used for the main thread to tell the thread that "this directory will be needed" by passing it the path name; the thread checks the queue even when loading images (for example, after each image or so) and switches to a new directory whenever it appears

  • you can create a directory reader object that saves all the state and saves it indexed along the way to the dictionary. When a new directory is requested, first check this dictionary and create a new object if there is no one for this directory. Thus, partially loaded directories will stick until they are needed again, and may continue to load, rather than starting from scratch.

Pseudocode for stream:

 while (forever) new element = nil if we have an active directory loader tell directory loader to load one image if false then make directory loader inactive lock queue condition if queue has elements new element = retrieve LAST element (we aren't interested in the others) empty queue unlock with status "empty" else unlock queue else lock queue on condition "has elements" new element = retrieve last element empty queue unlock with status "empty" if new element != nil if directory loader for new path does not exist setup new directory loader for new path store in dictionary make it the "active" one else make the current one the "active" 

As for the directory loader, it might look something like this:

 read one image: if there are still images to read: read, process and store one return true else performSelectorOnMainThread with an "update GUI" method and the image list as parameter return false; 

This is just a quick sketch; there is some duplication of code in the stream, and the way I wrote it will only update the graphical interface after all the images have been read, instead of displaying them when we read them. You will need to copy the current list of images or add synchronization if you want to do this.

0
source

Source: https://habr.com/ru/post/920549/


All Articles