The exploration of block in Objective-c

Source: Internet
Author: User


recently saw a lot of block related articles, are saying block how to use, writing is very exciting.
Blogs:
What to note about block programming (use related)
http://www.cocoachina.com/macdev/cocoa/2013/0527/6285.html
probe into block implementations in iOS (internal structure analysis)
Http://blog.csdn.net/jasonblog/article/details/7756763?reload
also shared by thread-bin (internal structure analysis)
https://www.evernote.com/shard/s269/sh/23b61393-c6dd-4fa2-b7ae-306e9e7c9639/ 131de66a3257122ba903b0799d36c04c?notekey=131de66a3257122ba903b0799d36c04c&noteguid= 23b61393-c6dd-4fa2-b7ae-306e9e7c9639
and read a book on this:
Pro Multithreading and Memory Management for IOS and OS X
Http://vdisk.weibo.com/s/9hjAV

I think it's time to summarize my understanding of block.
Note: The information provided above has a lot of useful background knowledge such as block how to use, when should add __block keyword declaration variable, how to solve the circular reference, what is heap, what is stack and so on, everybody writes better than me, I do not copy paste. The following text is some of my personal understanding, if there is a wrong place also please correct me.

1, block is a what?
Simply put, block is an "imitation" object.

in Objective-c, the class is inherited NSObject, nsobject inside will have an Isa, is a objc_class pointer.
and the object of block, in the C + + rewrite of clang,

^int(){printf("val"); return 111;};
This block will be converted to

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __testBlock_block_impl_0 {
  struct __block_impl impl;
  struct __testBlock_block_desc_0* Desc;
  __testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
__testBlock_block_impl_0 is a block structure, and its first attribute is also a structure __block_impl, and the first parameter is also a pointer to isa.
At runtime, the isa pointers of NSObject and block both point to a 4-byte object.
The isa of NSObject and derived class objects points to the prototype of the Class, and the isa of the block points to the pointer _NSConcreteStackBlock.
That is to say, when a block is declared, it is an object of _NSConcreteStackBlock class.

2. The lifetime of the block object
Usually in Objective-C, objects are declared on the heap.
When we run

NSString *str = [[NSString alloc] init];
At the time, the NSString is registered on the heap, and the object will not be killed until the reference count is reduced to 0 at the time of release.

Let’s take a look at the internal implementation of the block. When we implement

    {
        void (^testBlock) (void) = ^{printf("Look at this block");};
        testBlock();
    }
Will be converted to

    {
        void (*testBlock) (void) = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);
        ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
    }
 It can be seen that the block is declared on the stack, which means that when the block exceeds this "}", the block object will be recycled.

Let's do an experiment:
Objective-C source code (non-ARC)

    block stackBlock;
    {
        int val = 10;
        stackBlock = ^{NSLog(@"val = %d", val);};
    }
    stackBlock();
After conversion:

    block stackBlock;
    {
        int val = 10;
        stackBlock = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val);
    }
    ((void (*)(__block_impl *))((__block_impl *)stackBlock)->FuncPtr)((__block_impl *)stackBlock);
The above program runs without crashing.
From the result of this conversion, __main_block_impl_0, the object declared on the stack, should be released at the end of "}", but it can still be used in the following call?
I think this is luck. Personally, I don't recommend using the block object after the stack releases the block object.
Look at the declaration of __main_block_impl_0

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int flags=0): val(_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
This structure only has a constructor function, not a destructor function. As a result, the internal variables of the object are not assigned when the object pops up, so the addresses floating outside are all wild pointers.
(Note: The clang command is not completely reliable, it can only be used as a reference, because the C++ files converted by this tool cannot be compiled, and I feel that it can only be used as a research reference)

3. The block is passed on and assigned to the stack
Let’s take a look at the time of bad luck:
To prepare, get a new iOS project (non-ARC), and then define a blk type in ViewController.m.

typedef void (^blk) (void);
After getting a property

@interface ViewController () {
      blk tempBlock;
}
@end
Add a button to viewDidLoad and declare a block pointer to be paid to tempBlock

-(void)viewDidLoad
{
    [superviewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    
    UIButton *btn = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    btn.frame = CGRectMake(100.0f, 100.0f, 100.0f, 30.0f);
    [btn setTitle: @"Try" forState: UIControlStateNormal];
    [btn addTarget: self
            action: @selector(btnClick:)
  forControlEvents:UIControlEventTouchUpInside];
    [self.viewaddSubview: btn];
    
    __blockint val = 10;
    tempBlock = ^{NSLog(@"val = %d", ++val);};
}
Button click event:

-(void) btnClick: (id) Sender {
    tempBlock();
}
When the page is displayed normally, clicking the button will inevitably crash.
Prompt error: address doesn’t contain a section that points to a section in a object file
The reason is that the object pointed to by tempBlock has been recycled.
In Objective-C, this situation may also be that the object pointed to by tempBlock is autoreleased?
So let's just give tempBlock to retain, isn't it?

tempBlock = [^{NSLog(@"val = %d", ++val);} retain];
The result remains the same, but where is the problem?
I think this is because the block is a "fake" object. From the previous analysis, the isa of the block points to not object_class, but _NSConcreteStackBlock. I think this prototype redefines the retain/copy/ we are familiar with. Release and other functions defined by NSObject.
In the definition of the _NSConcreteStackBlock class, the retain function does not do any operation on the pointer, so it will not affect it. (The same for release)

In the various usage instructions of block, there is one. When the block is to be passed as a parameter or assigned, copy must be called. Let's compare the changes of the block before and after copy.
Modify the experiment just now:

-(void)viewDidLoad
{
    [superviewDidLoad];

     //Generate button (omitted)
    
    NSLog(@"_NSConcreteStackBlock %@", [_NSConcreteStackBlock class]);
    
    __block int val = 10;
    blk stackBlock = ^{NSLog(@"val = %d", ++val);};
    NSLog(@"stackBlock: %@", stackBlock);
    
    tempBlock = [stackBlock copy];
    NSLog(@"tempBlock: %@", tempBlock);
}
Printed result:

2013-05-29 14:21:09.969 BlockTest[2070:c07] _NSConcreteStackBlock __NSStackBlock__
2013-05-29 14:21:09.970 BlockTest[2070:c07] stackBlock: <__NSStackBlock__: 0xbfffdb28>
2013-05-29 14:21:09.970 BlockTest[2070:c07] tempBlock: <__NSMallocBlock__: 0x756bf20>
After copying, the type of the object changed from __NSStackBlock__ to __NSMallocBlock__
In the design of Objective-C, I have never seen copy change the type of the object once again, which shows once again that block is a special object.
Everyone should pay attention to the variable marked by __block. This variable will be piled up together with the block object. The blogs and books in this section have explanations, so I won't describe it.
There is also a type of block type __NSGlobalBlock__. When there are no local variables in the block, the block will become this type. The retain/copy/release of this type will not affect the object and can be understood as a static block.
The __NSMallocBlock__ object is copied again, no new objects will be generated but the original objects will be retained.
After experiments, the functions of retain/copy/release of several block types are as follows (non-ARC environment):


4. Things are not over yet
When a block object is piled up, its life cycle is the same as that of an ordinary NSObject object (this should be __NSMallocBlock__ The design of this class refers to the design of the NSObject object)
As a qualified Objective-C programmer, you should think of release when you see copy.
In a non-ARC environment, after copying the block, it must be released after use, otherwise there will be memory leaks, and the leak point is at the system level, and the trigger point of the problem cannot be tracked in Instruments, which is more popular.


Another issue I want to discuss here is the design principle. For an object, we will always want to autorelease the object when it is transmitted, such as:

-(NSArray *) myTestArray

{
    NSArray *array = [[NSArray alloc] initWithObjects: @"a", @"b", @"c", nil];
    return [array autorelease];
}
Similarly, we must do this when we pass the block outward, to an autorelease object on the heap.

-(blk) myTestBlock {
    __blockint val = 10;
    blk stackBlock = ^{NSLog(@"val = %d", ++val);};
    return [[stackBlock copy] autorelease];
}
The first step is to copy the block from the stack to the heap. The second step is to autorelease to prevent memory leaks.

Similarly, sometimes we will put the block in another class for callback, such as the callback in AFNetworking.
At this time, according to the unified design principle, we should also give the calling object an autorelease object on the heap.

In short, when transferring the block object to the outside, we have to send out a __NSMallocBlock__ object that has been copied and then autoreleased on the heap. (Personal opinion, block is invented after imitating the NSObject object, so don’t let the caller do something different from other objects)

5. Talk about ARC
The above methods are all precautions for non-ARC programming. Many rules under ARC can be omitted.
Because there is a principle under ARC, as long as the block passes under the strong pointer, it will be put on the heap.
Look at the following experiment:

    {
        __blockint val = 10;
        __strong blk strongPointerBlock = ^{NSLog(@"val = %d", ++val);};
        NSLog(@"strongPointerBlock: %@", strongPointerBlock); //1
        
        __weak blk weakPointerBlock = ^{NSLog(@"val = %d", ++val);};
        NSLog(@"weakPointerBlock: %@", weakPointerBlock); //2
        
        NSLog(@"mallocBlock: %@", [weakPointerBlock copy]); //3
        
        NSLog(@"test %@", ^{NSLog(@"val = %d", ++val);}); //4
    }
Obtained log

2013-05-29 16:03:58.773 BlockTest[3482:c07] strongPointerBlock: <__NSMallocBlock__: 0x7625120>
2013-05-29 16:03:58.776 BlockTest[3482:c07] weakPointerBlock: <__NSStackBlock__: 0xbfffdb30>
2013-05-29 16:03:58.776 BlockTest[3482:c07] mallocBlock: <__NSMallocBlock__: 0x714ce60>
2013-05-29 16:03:58.777 BlockTest[3482:c07] test <__NSStackBlock__: 0xbfffdb18>
Analyze:
The block pointed to by the strong pointer has been put on the heap.
The block pointed to by the weak pointer is still on the stack (this declaration method is only valid on the block, the normal weak pointer points to the object on the heap, it will directly become nil, you need to use the strong pointer to pass one, please refer to the ARC pointer use notes )
The third line of log is the same as non-ARC, moving the block from the stack to the heap.
The last line of the log indicates that when the block is declared separately, the block will still be on the stack.

In another case under ARC, the block is returned as a parameter

-(__unsafe_unretained blk) blockTest {
    int val = 11;
    return ^{NSLog(@"val = %d", val);};
}
Caller

NSLog(@"block return from function: %@", [self blockTest]);
Obtained log:

2013-05-29 16:09:59.489 BlockTest[3597:c07] block return from function: <__NSMallocBlock__: 0x7685640>
Analyze:
In the ARC environment, when the block is returned as a parameter, the block will also be automatically moved to the heap.

Under ARC, as long as the pointer passes the strong pointer, or the function returns, the block will be moved to the heap.
So passing the strong pointer before passing the block to the callback party can satisfy the design principles I just described.

to sum up:
The text above introduces:
1. The structure of block in Objective-C environment
     block is a "fake" object
2. The lifetime of the block statement
     The declared object on the stack will be recycled. If you want to hold the block object for a long time, please move it to the heap
3. When to convert from stack to heap
     When will the blocks on the stack be moved to the heap during copy? There are three types of blocks
4. Some design guidelines that I personally understand
     Give the caller an autoreleased block object on the heap.
5. Some precautions under ARC
     After the strong pointer, he is good, and I am good.


    Copyright ©2015 Rock Poet
Pursuit of Block in Objective-C


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.