There is a Block called Callback, and there is a Callback called CompletionHandler

There is a Block called Callback, and there is a Callback called CompletionHandler

[Introduction] The APIs of the iOS 10 push part use the naming method of CompletionHandler extensively. In this article, we will compare the particularity of this Block to better understand and practice the CompletionHandler style Block in your own projects.

text

When we integrate a Lib (also called a wheel, SDK, and Lib in the following text), as a developer, we will find that the Blocks we encounter can be divided into the following types according to their functions:

  • Lib notifies the developer that the Lib operation has been completed. Generally named Callback
  • The developer notifies the library that the developer's operation has been completed. Generally, it can be named CompletionHandler.

The difference between these two places: the former is "Block execution" and the latter is "Block filling".

Apple has not clearly pointed out the difference in naming and functions between Callback and CompletionHandler in its coding standards. However, if we divide the functions into "execution and filling", the naming of callback and completionHandler can be treated separately. It is also convenient for the caller to understand the function of the block. But in general, in Apple's official naming, the "Block filling" function is generally named "completionHandler", and the "Block execution" function is mostly named "callback", and a small number of them are named "completionHandler".

for example:

In NSURLSession, the following function names the "callback" as "completionHandler":

  1. - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;

We often see CompletionHandler being used in the first scenario, while the first scenario "Block execution" is more appropriately named Callback.

Not all blocks are suitable to be called CompletionHandler

In general, the design of CompletionHandler often takes multi-threaded operations into consideration, so you can operate asynchronously and then execute the CompletionHandler at the end of the thread. The following example will describe some advantages of the CompletionHandler method in multi-threaded scenarios.

CompletionHandler + Delegate combination

This type of Block is widely used in the newly added UserNotificaitons in iOS10, for example:

  1. - (void)userNotificationCenter:(UNUserNotificationCenter *)center
  2. didReceiveNotificationResponse:(UNNotificationResponse *)response
  3. withCompletionHandler:(void (^)(void))completionHandler;

The documentation for completionHandler is as follows:

  1. The block to   execute   when you have finished processing the user 's response. You must execute this block from your method and should call it as quickly as possible. The block has no   return value or parameters.

The same applies here:

  1. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
  2. didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
  3. completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler;

There is another very common example (one of the four essential proxy functions when using URLSession in Delegate mode)

  1. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
  2. didReceiveResponse:(NSURLResponse *)response
  3. completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;

In the proxy method implementation code, if completionHandler(NSURLSessionResponseAllow) is not executed, the http request is terminated.

CompletionHandler + Block combination

A function that uses a function as a parameter or return value is called a higher-order function.

According to this definition, Block takes Block as a parameter, which is a high-order function.

Let's take a look at an example based on a practical application scenario:

If there is such a requirement:

Take my previous IM project ChatKit-OC (open source, referred to as ChatKit below) as an example. When your application wants to integrate an IM service, your APP may have been launched at this time, and it already has its own registration and login processes. Chatting with ChatKit is very simple. You only need to give ChatKit an id. The chat is normal, but both parties can only see one id, which is a bad experience. But how to display avatars and nicknames? So I designed such an interface, -setFetchProfilesBlock:.

This is the block where the upper layer (APP) provides user information. Since ChatKit does not care about business logic information, such as user nicknames, user avatars, etc., users can inject a user information content provider block into ChatKit through the ChatKit singleton. Only through this user information provider block can ChatKit correctly draw business logic data.

The schematic diagram is as follows:

The specific implementation is as follows:

The method definition is as follows:

  1. /*!
  2. * @brief The block to   execute   with the users ' information for the userIds. Always execute this block at some point when fetching profiles completes on main thread. Specify users' information how you want ChatKit to show.
  3. * @attention If you fetch users fails, you should reture nil, meanwhile, give the error reason.
  4. */
  5. typedef void(^LCCKFetchProfilesCompletionHandler)(NSArray> *users, NSError *error);
  6.   
  7. /*!
  8. * @brief When LeanCloudChatKit wants to   fetch profiles, this block will be invoked.
  9. * @param userIds User ids
  10. * @param completionHandler The block to   execute   with the users ' information for the userIds. Always execute this block at some point during your implementation of this method on main thread. Specify users' information how you want ChatKit to show.
  11. */
  12. typedef void(^LCCKFetchProfilesBlock)(NSArray *userIds, LCCKFetchProfilesCompletionHandler completionHandler);
  13.   
  14. @property (nonatomic, copy) LCCKFetchProfilesBlock fetchProfilesBlock;
  15.   
  16. /*!
  17. * @brief Add the ablitity to   fetch profiles.
  18. * @attention You must get peer information by peer id with a synchronous implementation.
  19. * If implemeted, this block will be invoked automatically by LeanCloudChatKit for fetching peer profile.
  20. */
  21. - (void)setFetchProfilesBlock:(LCCKFetchProfilesBlock)fetchProfilesBlock;

The usage is as follows:

  1. #warning Note: The setFetchProfilesBlock method must be implemented. If it is not implemented, ChatKit will not be able to display user avatars and user nicknames. The following method simulates the process of synchronously querying users information through userIds. Here you need to replace it with the App API synchronous query
  2. [[LCChatKit sharedInstance] setFetchProfilesBlock:^(NSArray *userIds,
  3. LCCKFetchProfilesCompletionHandler completionHandler) {
  4. if (userIds. count == 0) {
  5. NSInteger code = 0;
  6. NSString *errorReasonText = @ "User ids is nil" ;
  7. NSDictionary *errorInfo = @{
  8. @ "code" :@(code),
  9. NSLocalizedDescriptionKey : errorReasonText,
  10. };
  11. NSError *error = [NSError errorWithDomain:NSStringFromClass([self class])
  12. code:code
  13. userInfo:errorInfo];
  14.   
  15. !completionHandler ?: completionHandler(nil, error);
  16. return ;
  17. }
  18.   
  19. NSMutableArray *users = [NSMutableArray arrayWithCapacity: userIds.count ];
  20. #warning Note: The following method loop simulates the process of synchronously querying users information through userIds. It needs to be replaced with the synchronous query of the App API.
  21.   
  22. [userIds enumerateObjectsUsingBlock:^(NSString *_Nonnull clientId, NSUInteger idx,
  23. BOOL *_Nonnull stop) {
  24. NSPredicate *predicate = [NSPredicate predicateWithFormat:@ "peerId like %@" , clientId];
  25. //The LCCKContactProfiles and LCCKProfileKeyPeerId here are macro definitions in advance.
  26. NSArray *searchedUsers = [LCCKContactProfiles filteredArrayUsingPredicate:predicate];
  27. if (searchedUsers. count > 0) {
  28. NSDictionary * user = searchedUsers[0];
  29. NSURL *avatarURL = [NSURL URLWithString: user [LCCKProfileKeyAvatarURL]];
  30. LCCKUser *user_ = [LCCKUser userWithUserId: user [LCCKProfileKeyPeerId]
  31. name : user [LCCKProfileKeyName]
  32. avatarURL:avatarURL
  33. clientId:clientId];
  34. [users addObject:user_];
  35. } else {
  36. // NOTE: If the network request fails, please at least provide the ClientId!
  37. LCCKUser *user_ = [LCCKUser userWithClientId:clientId];
  38. [users addObject:user_];
  39. }
  40. }];
  41. //Simulate network delay, 3 seconds
  42. // sleep(3);
  43.   
  44. #warning Important: The completionHandler Bock must be executed. You need to pass the information to this Block after you **finish obtaining user information**!
  45. !completionHandler ?: completionHandler([users copy], nil);
  46. }];

For the application scenario of the above Fetch method, the return value of the actual method can also be realized, but compared with CompletionHandler, the inability to switch threads freely is a disadvantage.

<<:  iOS Auto Layout Framework – Masonry Detailed Explanation

>>:  Android keyboard panel conflict layout flashing solution

Recommend

How about opening an account with Baidu Advertising?

Now there are still a lot of businesses who see t...

Sharing APP operation experience, 6 ways to increase user activity!

How to maintain and increase activity and improve...

Jizhou SEO training: Internet marketing promotion in industrial cities

I believe that when we surf the Internet, we will...

Use functional Swift to convert images to characters

Today, I was sorting out the articles to be read ...

Download Douyin Live Streaming Crash Course Baidu Cloud

Download Douyin Live Streaming Crash Course Baidu...

A picture to understand the front-end performance optimization of Html5

Overview 1. PC optimization methods are also appl...

Web Development Trends in 2015

[[126116]] The Web was originally conceived as a ...