“Writing High Quality OC Code” has been successfully completed chapter one, two, three, four, five, six, eight! Attach a link: Objective-c code for iOS (1) — Objective C code for iOS (2) — Objective C code for iOS (3) — Interface and API design Objective-c code (5) — Memory management mechanism (6) — Block column iOS Writing high quality Objective-C code for iOS (GCD


The topic of this article is “How blocks work and How they work” in iOS.

A brief introduction to today’s hero: Block.

  • Block: A “lexical closure” through which a developer passes a block of code as an object.

Understanding the concept of “block” :

1. Block data structure:

Using the clang command line tool (OC to C++), let’s take a look at the internal data structure of the block.

struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); }; struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...) ; struct Block_descriptor *descriptor; /* Imported variables. */ };Copy the code

Block_layout isa structure that contains an isa pointer to the Class object. There is also a function pointer that points to the block’s implementation code.


2. There are three types of blocks: global block, stack block, and heap block.

Blocks are divided into three types based on their location in memory:

type Memory location introduce
__NSStackBlock__ The stack area Stack valid, out of the stack after destruction.
__NSMallocBlock__ The heap area Copy to the heap space. It can be used outside of the defined scope.
__NSGlobalBlock__ The global area Without capturing any external variables, all information is determined at the compiler.

  • 1. NSStackBlock:Stack blocks are stored in the stack area, out of scope, on the stackblockAnd declarative_blockWill be destroyed.

Such as:

__block NSString *name = @"QiShare";
void (^block)(void) = ^{
    NSLog(@"%@ is an iOS team which loves to share technology.", name);
};
NSLog(@"block = %@", block);
Copy the code

Note: When an external variable needs to be modified or accessed from within a block, the external variable needs to be modified with an additional __block modifier. Otherwise, it cannot be modified.

Let’s take a look at printing:

What? __NSMallocBlock__? That’s because the compiler automatically added a copy operation for us in ARC.

Set Objective-C Automatic Reference Counting = NO. Take a look at printing:


  • 2. NSMallocBlock heap block: The heap block memory is located in the heap and is still available when the variable scope ends.

From the example above: Under ARC, block will be copy by default: __NSMallocBlock__.


  • NSGlobalBlock: There are no external objects in the block. The required memory is determined at compile time. Memory is located in the global area. Like a singleton, copy is an empty operation.

Such as:

void (^qiShare)(void) = ^{
    
    NSLog(@"We love sharing.");
};
NSLog(@"% @",qiShare);
Copy the code

Create a typedef for a common block type

To increase the readability and extensibility of your code, you need to give common blocks individual names.

Using typedefs as aliases for blocks also makes it easier to use block variables. For example:

- (void)getDataWithToken:(NSString *)token success:(void (^)(id responseDic))success; / /! Typedef void (^SuccessBlock)(id responseDic); - (void)getDataWithToken:(NSString *)token success:(SuccessBlock)success;Copy the code

Use handler blocks to reduce code dispersion

In our iOS development, it’s common to execute tasks asynchronously, wait until the task is finished, and then tell objects to call methods. There are two ways to do this:

  • First: use NSNotificationCenter:NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  • Second: Use a delegate protocol: See Writing High-quality Objective-C code for iOS (Part 4).
  • Third: Using block callbacks: Directly pass the block object as an argument to the related method.

For example: The API design and use of AFNetworking is the Block callback

  • Interface design:
- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(id)parameters
                       success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                       failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure {

    return [self POST:URLString parameters:parameters progress:nil success:success failure:failure];
}
Copy the code
  • Use:
AFHTTPSessionManager *manger =[AFHTTPSessionManager manager]; NSString *urlString = @""; NSMutableDictionary *parameter= @{@"":@"",@"":@""}; [manger POST:urlString parameters:parameter success:^(NSURLSessionDataTask * _Nonnull task, Id _Nullable responseObject) {NSLog(@" success ");  } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@",error); }]; }Copy the code

Avoid circular references when a block refers to an object it belongs to

In our daily development, blocks can easily leak memory if not used properly.

  • Reason: IfblockBy the current ViewController (selfIf the block holds a ViewController (self), creating a circular reference.
  • Solution: InblockexternalweakeningselfAnd then inside the blockstrengtheningweakenedweakSelf

For Example:

__weak typeof(self) weakSelf = self;

[self.operationQueue addOperationWithBlock:^{

    __strong typeof(weakSelf) strongSelf = weakSelf;

    if (completionHandler) {

        KTVHCLogDataStorage(@"serial reader async end, %@", request.URLString); completionHandler([strongSelf serialReaderWithRequest:request]); }}];Copy the code

Of course, not all blocks that use self should be first weakened into weakSelf and then strengthened into strongSelf. As long as the block is not held by self, self can be used in the block. For example:

[QiNetwork requestBlock:^(id responsObject) {
      NSLog(@"% @",self.name);
  }];
Copy the code

For details about memory leak detection, see iOS Memory Leak Detection and Cause Analysis