[Introduction] iOS10 push part of the API, a large number of the use of CompletionHandler this way of naming, so this paper we will compare the particularity of this Block, in order to better understand and practice in their own projects CompletionHandler style Blcok.
There is a Block called Callback, and a Callback called CompletionHandler.
The body of the
When we, as developers, integrate a Lib (also called wheel, SDK, Lib), we find that the blocks we encounter can be divided into these types in terms of functionality:
- Lib notifies the developer that the Lib operation is complete. The common name is Callback
- The developer notifies Lib that the developer’s action is complete. You can usually name it CompletionHandler.
The difference between the two is that the former is “execution of a Block” and the latter is “filling of a Block”.
Callback vs CompletionHandler There is no specific coding specification for the difference between naming and function, but if it is divided into “execute and fill” functions, The names of callback and completionHandler can be treated separately. It also makes it easier for the caller to understand what a block does. But in general, in Apple’s official naming, the “Block fill” function is usually named “completionHandler” and the “Block execute” function is mostly named “callback”. A few are also named “completionHandler.”
Such as:
In NSURLSession, the following function names “callback” to “completionHandler” :
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
Copy the code
We often see CompletionHandler used in the first scenario, and the first scenario, “Block execution,” is more appropriately named Callback.
Not all blocks are properly called CompletionHandler
Normally, CompletionHandler is designed with multithreading in mind, so you can do it asynchronously, and then execute that CompletionHandler at the end of the thread, The examples below describe some of the advantages of the Complete Handler approach in multi-threaded scenarios.
CompletionHandler + Delegate
The new UserNotificaitons in iOS10 make extensive use of these blocks, such as:
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler;Copy the code
The document comments completionHandler like this:
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.Copy the code
It also works here:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler;Copy the code
There is another example that is also very common (one of the four essential Delegate functions when using URLSession in Delegate mode)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;Copy the code
Inside the method to realize the code, if does not perform completionHandler (NSURLSessionResponseAllow), HTTP request is annulled.
CompletionHandler + Block
Functions that take functions as arguments or return values are called higher-order functions.
By this definition, a Block takes a Block as an argument, which is a higher-order function.
Take a look at an example based on the actual application scenario:
If there is a need:
Take chatKit-OC, one of my previous IM projects, for example. If you want to integrate an IM service with your APP, you may already have your APP on the APP store and already have its own registration, login, etc. Chatting with ChatKit is easy, just give ChatKit an ID. Chat is normal, but both sides can only see one ID, this experience is not good. But how do you display your avatar or nickname? An interface is designed, -setFetchProfilesBlock:.
This is a Block where the upper layer (APP) provides user information, since ChatKit doesn’t care about business logic such as user nicknames, user avatars, etc. You can use the ChatKit singleton to inject a Block of user information into the ChatKit. After the Block is provided, the ChatKit can draw logical service data correctly.
The schematic diagram is as follows:
The concrete implementation is as follows:
The method is defined as follows:
/ *! * @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. * @attention If you fetch users fails, you should reture nil, meanwhile, give the error reason. */ typedef void(^LCCKFetchProfilesCompletionHandler)(NSArray<id<LCCKUserDelegate>> *users, NSError *error); / *! * @brief When LeanCloudChatKit wants to fetch profiles, this block will be invoked. * @param userIds User ids * @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. */ typedef void(^LCCKFetchProfilesBlock)(NSArray<NSString *> *userIds, LCCKFetchProfilesCompletionHandler completionHandler); @property (nonatomic, copy) LCCKFetchProfilesBlock fetchProfilesBlock; / *! * @brief Add the ablitity to fetch profiles. * @attention You must get peer information by peer id with a synchronous implementation. * If implemeted, this block will be invoked automatically by LeanCloudChatKit for fetching peer profile. */ - (void)setFetchProfilesBlock:(LCCKFetchProfilesBlock)fetchProfilesBlock;Copy the code
The usage is as follows:
#warning: setFetchProfilesBlock must be implemented. If not, ChatKit will not be able to display the user's avatar or nickname. The following method iterates the process of querying users information synchronously through userIds, [LCChatKit sharedInstance] setFetchProfilesBlock:^(NSArray<NSString *> *userIds, LCCKFetchProfilesCompletionHandler completionHandler) { if (userIds.count == 0) { NSInteger code = 0; NSString *errorReasonText = @"User ids is nil"; NSDictionary *errorInfo = @{ @"code":@(code), NSLocalizedDescriptionKey : errorReasonText, }; NSError *error = [NSError errorWithDomain:NSStringFromClass([self class]) code:code userInfo:errorInfo]; ! completionHandler ?: completionHandler(nil, error); return; } NSMutableArray *users = [NSMutableArray arrayWithCapacity:userIds.count]; # warning notice: The following method iterates the process of querying users information synchronously through userIds, There needs to be replaced with the App synchronous query API [userIds enumerateObjectsUsingBlock: ^ (nsstrings * _Nonnull clientId, NSUInteger independence idx. BOOL *_Nonnull stop) { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"peerId like %@", clientId]; // LCCKContactProfiles, LCCKProfileKeyPeerId are predefined macros, NSArray *searchedUsers = [LCCKContactProfiles filteredArrayUsingPredicate:predicate]; if (searchedUsers.count > 0) { NSDictionary *user = searchedUsers[0]; NSURL *avatarURL = [NSURL URLWithString:user[LCCKProfileKeyAvatarURL]]; LCCKUser *user_ = [LCCKUser userWithUserId:user[LCCKProfileKeyPeerId] name:user[LCCKProfileKeyName] avatarURL:avatarURL [users addObject:user_];} else {// note: If the network request fails, please at least provide ClientId! LCCKUser * user_ = [LCCKUser userWithClientId: clientId]; [users addObject: user_].}}]; / / simulation network latency, 3 seconds / / sleep (3); This Bock must be executed and needs to be passed to the Block after you ** get the user information. CompletionHandler? completionHandler([users copy], nil); }];Copy the code
For the application scenario of the Fetch method above, the return value of the method can also be implemented, but compared to the CompletionHandler, the inability to switch threads freely is a disadvantage.
There is a Block called Callback, and a Callback called CompletionHandler.
Posted by weibo @ios program dog Yuan