With NSProgress - completion handlers designed for a user interface or boot controller?

I use NSProgress to report the progress of file downloads in my iOS application. This is a very versatile class, and I'm a little afraid of its inherent power, especially with two completion processing properties. There is one to fight the cancellation, and the other to pause (but to complete, but maybe not for this ...)

What are these handlers intended for? The code that performs the download may cause the logic to work with errors that were canceled and canceled by users. However, there is nothing to stop the client from overwriting handlers using the user interface code.

So is it designed for user interface? I'm not sure if these are useful templates, as in any case the user interface will create a cancellation or pause. In addition, if you use a progress object to simultaneously present progress across multiple user interface elements (the way it is used on MacOS), various user interface elements may potentially require its own completion handler.

Using handlers to pass custom actions back to the boot controller seems like the most useful template, but then I expected the handler to be configured during initialization and then remain read-only.

What am I missing here?

(PS At the moment, I'm just not going to use these handlers and rely on KVO. However, I have an itchy feeling that I am missing some fundamental idea behind the class)

+6
source share
2 answers

I believe that the key that you are missing is that the NSProgress class NSProgress designed to be used as a tree of progress objects. In addition, this tree is created implicitly with child objects of progress that do not need to realize that they are attached to the parent object, from where the real power comes from.

I found Foundation OS X release notes much more useful than the NSProgress class NSProgress :

https://developer.apple.com/library/Mac/releasenotes/Foundation/RN-Foundation/index.html

The reason it seems that handlers can be used for both UI controller logic and data controller logic is that when building the parent-child hierarchy, you have two sets of handlers that can be used for both. Parent handlers will be installed at the UI controller level ("consumer" of progress), and child handlers will be installed at the data controller ("provider").

Since a link can be created implicitly using becomeCurrentWithPendingUnitCount: child process object will be isolated from the parent, which will alleviate your concern that clients overwrite any data-level handlers with their own.

A call to pause or cancel a progress object will propagate this call down the tree, invoking any handlers along the way.

Example:

 // UI controller level, probably a UIViewController subclass. - (void)handleDoSomethingButtonTapped:(UIButton *)sender { self.progressThatWeObserve = [NSProgress progressWithTotalUnitCount:100]; // 100 is arbitrary self.progressThatWeObserve.pausingHandler = ^{ // Update UI, reflect paused state ... }; [self.progressThatWeObserve becomeCurrentWithPendingUnitCount:100]; [self.dataController doSomethingInBackgroundWithCompletionHandler:^{ // Update UI, remove from view ... }]; [self.progressThatWeObserve resignCurrent]; } // Data controller level, a SomethingManager class maybe. - (void)doSomethingInBackgroundWithCompletionHandler:(void (^)(void)completionHandler { self.progressThatWeManipulate = [NSProgress progressWithTotalUnitCount:289234]; // eg bytes to upload self.progressThatWeManipulate.pausingHandler = ^{ // Actually suspend the network operation ... }; dispath_async(self.workerQueue, ^{ // Periodically update progress }); } 

Please note that I actually did nothing, this is a theory from the documentation for reading.

+1
source

You can use these blocks for anything. In particular, if you want to do something with the user interface, you should do it in the main thread, as indicated in the "Special Considerations" section of the documentation: "The cancellation handler can be called in any queue. Work in a specific queue, you must send to this queue from the undo handler block. " Therefore, for the user interface, you will use:

 dispatch_async(dispatch_get_main_queue(), ^{ // Do work on UI }); 

The completion handler template is common for solving tasks that always run in the background; I / O tasks, such as downloading a file from the server. In this case, although it is also useful to define one handler for pausing and canceling, since the application can have many events that cause pause: or cancel: calls on an NSProgress instance.

0
source

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


All Articles