The type of the Block

  • There are three types of blocks:

    • NSGlobalBlock (NSConcreteGlobalBlock)
    • NSStackBlock (NSConcreateStackBlock)
    • NSMallocBlock (NSConcreateMallocBlock)
  • You can see the type through a class or ISA pointer, which ultimately inherits from the NSBlock type

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^block)(void) = ^{
            NSLog(@"Hello");
        };
        
        NSLog(@"% @", [block class]);
        NSLog(@"% @", [[block class] superclass]);
        NSLog(@"% @", [[[block class] superclass] superclass]);
        NSLog(@"% @", [[[[block class] superclass] superclass] superclass]);
        
    }
    return 0;
}

Copy the code
2020-06-03 21:42:23.579757+0800 Interview01-block nature [56222:9306988] __NSGlobalBlock__ 2020-06-03 21:42:23.581321+0800 [56222:9306988] __NSGlobalBlock 2020-06-03 21:42:23.581831+0800 Interview01-block [56222:9306988] NSBlock 2020-06-03 21:42:23.585482+0800 Interview01-Block essence [56222:9306988] NSObjectCopy the code

Because it inherits from NSObject, blocks are isa Pointers, so you can also think of a block as an object

Print and compile the three blocks

Int main(int argc, const char * argv[]) {@autoreleasepool { Void (^block1)(void) = ^{NSLog(@"Hello");
        };
        
        int age = 10;
        void (^block2)(void) = ^{
            NSLog(@"Hello - %d", age);
        };
        
        NSLog(@"% @ % @ % @", [block1 class], [block2 class], [^{
            NSLog(@"%d", age);
        } class]);
        return0; }}Copy the code

Print the result

2020-06-03 21:48:28.625843+0800 Interview01-block nature [56274:9310988] __NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__Copy the code

$xcrun – SDK iphoneOS clang –arch arm64 rewrite-objc main.m

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; struct __main_block_impl_1 { struct __block_impl impl; struct __main_block_desc_1* Desc; int age; __main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; struct __main_block_impl_2 { struct __block_impl impl; struct __main_block_desc_2* Desc; int age; __main_block_impl_2(void *fp, struct __main_block_desc_2 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

Notice that this is all _NSConcreteStackBlock. It’s not the same as the runtime, but everything is the result of the runtime, because the runtime may have done some processing, and the compiled code may not be real OC code


Block memory allocation

Application memory addresses are sorted from low to high (highest stack)

  • .text segment: code
  • .data segment: global variable
  • Heap: dynamically allocates memory, programmers apply for memory, and manages memory
  • Stack: local variables that are destroyed when braces are left
int age_ = 10;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 10;
        NSLog(@"Data segment: age_ %p", &age_);
        NSLog(@"Stack: a %p", &a);
        NSLog(@"Heap: obj %p", [[NSObject alloc] init]);
        NSLog(@"Segment: class %p", [RLPerson class]);
            }
    return 0;
}
Copy the code

Print result:

2020-06-29 14:09:54.578086+0800 TestBlock[3428:10823423] 0x1000011D0 2020-06-29 14:09:54.578513+0800 TestBlock[3428:10823423] stack A 0x7ffeefbff51c 2020-06-29 14:09:54.578605+0800 TestBlock[3428:10823423] Obj 0x100502F80 2020-06-29 14:09:54.578705+0800 TestBlock[3428:10823423] Data segment: 0x1000011a8Copy the code

You can see that the RLPerson address is similar to the age_ address, so the class is stored in the data segment


The Block under the MRC

Disable ARC: Target -> Build Setting -> Objctive -c Automatic Reference Counting set to NO

Check to see if the block type of the auto variable is accessed

Int main(int argc, const char * argv[]) {@autoreleasepool {// Global: no access to auto void (^block1)(void) = ^{NSLog(@"block1---------"); }; // Stack: access the auto variable int age = 10; void (^block2)(void) = ^{ NSLog(@"block2---------%d", age);
        };
        
        NSLog(@"% @ - % @", [block1 class], [block2 class]);
    }
    return 0;
}

Copy the code

Print result:

[56499:9322480] __NSGlobalBlock__-__NSStackBlock__Copy the code

You can see the presence of __NSStackBlock__ of type __NSStackBlock__ that accesses the auto variable

void (^block)(void);
void test2()
{
   // NSStackBlock
   int age = 10;
   block = ^{
       NSLog(@"block---------%d", age);
   };
}

int main(int argc, const char * argv[]) {
  @autoreleasepool {
      test(2); block(); }return 0;
}

Copy the code

Print result:

2020-06-03 22:08:45.533995+0800 Interview01-Block essence [56553:9325730] Block ----------272632888Copy the code

The value of age is incorrect. After test2() is executed, the stack memory may be garbage, causing the printed value to be problematic

How to deal with this situation? Change the block to _NSMallocBlock, where __NSStackBlock__copy becomes _NSMallocBlock_. You can control the block’s life cycle in the heap.

void (^block)(void);
void test2()
{
    // NSStackBlock
    int age = 10;
    block = [^{
        NSLog(@"block---------%d", age);
    } copy];
    [block release];
}
Copy the code

Print the result

2020-06-03 22:13:27.853082+0800 Interview01-Block essence [56639:9329121] Block ---------10Copy the code

summary


Block in ARC environment

In an ARC environment, the compiler automatically copies blocks on the stack to the heap as appropriate, as in the following case

  • As a function return value
  • When block is assigned to the strong pointer _strong
  • Block is used as a Cocoa API method name that contains the usingBlock method argument
  • Block is a method parameter of the GCD API

As a function return value

typedef void (^RLBlock)(void);
RLBlock myblock()
{
    int a = 10;
    return ^{
        NSLog(@"---------%d", a);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"% @", [myblock() class]);
    }
    return 0;
}
Copy the code
2020-06-03 23:26:53.399930+0800 Interview01-block的copy[57002:9364496] __NSMallocBlock__
Copy the code
  1. When block is assigned to the strong pointer _strong
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        RLBlock block = ^{
            NSLog(@"---------%d", age);
        };

        NSLog(@"Strong pointer :%@", [block class]);
        NSLog(@"No strong pointer :%@", [^{
            NSLog(@"---------%d", age);
        } class]);
    }
    return 0;
}
Copy the code
A strong pointer to __NSMallocBlock__ 2020-06-03 23:29:30.468301+0800 Interview01-block copy[57030:9366298] No strong pointer to __NSStackBlock__Copy the code
  1. Block is used as a Cocoa API method name with usingBlock. NSMutableArray is not modified by __block
NSMutableArray *sorts = [NSMutableArray array];
[sorts enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
Copy the code
  1. Block is a method parameter of the GCD API
Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{});Copy the code

The auto variable of object type

Create a class

 
@interface RLPerson : NSObject
@property(nonatomic, assign) NSInteger age;
@end

@implementation RLPerson

- (void)dealloc
{
//    [super dealloc];
    NSLog(@"RLPerson - dealloc");
}

@end

typedef void (^RLBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RLBlock block;
        {
            RLPerson *person = [[RLPerson alloc] init];
            person.age = 10;
            block = ^{
                NSLog(@"---------%ld", (long)person.age);
            };
            NSLog(@"-- -- -- -- -- - % @", [block class]);
        }
        NSLog(@"-- -- -- -- --");
    }
    return 0;
}
Copy the code

Because of strong pointer references, all blocks are of type __NSMallocBlock__

Print the value at the break point to NSLog(@”—–“) in the ARC environment

2020-06-05 08:45:09.755390+0800 Interview01-block的copy[1125:71486] ------__NSMallocBlock__
Copy the code

In the MRC environment, the value is printed at the interrupt point to NSLog(@”—–“)

- (void)dealloc
{
    [super dealloc];
    NSLog(@"RLPerson - dealloc");
}

typedef void (^RLBlock)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RLBlock block;
        {
            RLPerson *person = [[RLPerson alloc] init];
            person.age = 10;
            block = ^{
                NSLog(@"---------%ld", (long)person.age);
            };
            [person release];
            NSLog(@"-- -- -- -- -- - % @", [block class]);
        }
        NSLog(@"-- -- -- -- --");
    }
    return 0;
}


Copy the code
2020-06-07 12:15:50.813657+0800 Interview01-block copy[1231:28255] RLPerson - dealloc 2020-06-07 12:16:10.955578+0800 Interview01 - block copy (1231-28255) -- -- -- -- -- - __NSStackBlock__Copy the code

Copy blocks in the MRC environment

block = [^{ 
             NSLog(@"---------%d", person.age);
          } copy];
Copy the code

Print the result

2020-06-07 12:18:58.412416+0800 Interview01-block的copy[1268:31279] ------__NSMallocBlock__
Copy the code

The block is of type __NSMallocBlock__, which is equivalent to [Person retain], so heap blocks can strongly reference holding objects, whereas stack blocks cannot

ARC continues the research by adding an __weak modifier to the person object

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RLBlock block;
        {
            RLPerson *person = [[RLPerson alloc] init];
            person.age = 10;
            __weak RLPerson *weakPerson = person;
            block = ^{
                NSLog(@"---------%ld", (long)weakPerson.age);
            };
            NSLog(@"-- -- -- -- -- - % @", [block class]);
        }
        NSLog(@"-- -- -- -- --");
    }
    return 0;
}

Copy the code

Break point to NSLog(@”—–“); Print the result and find that the Person has been destroyed

2020-06-07 12:38:46.093930+0800 Interview01-block copy[1540:52404] RLPerson - Dealloc 2020-06-07 12:38:46.094325+0800 Interview01 - block copy (1540-52404) -- -- -- -- -- - __NSMallocBlock__Copy the code

View the c++ source code execution command at this point

 $ xcrun -sdk iphoneos clang -arch -rewrite-objc main.m
Copy the code

Errors will be found

/var/folders/zz/v5rdxc250h53xq3b41vnsd3c0000gn/T/main-802857.mi:28844:28: error: 
      cannot create __weak reference because the current deployment target does
      not support weak references
            __attribute__((objc_ownership(weak))) RLPerson *weakPerson = person;
                           ^
1 error generated.
Copy the code

This is because you want to compile at runtime, not statically, using the following command

xcrun -sdk iphoneos clang -arch arm64  -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13 main.m
Copy the code

RLPerson *__weak weakPerson

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; RLPerson *__weak weakPerson; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, RLPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

If block is changed to print person

 block = ^{
           NSLog(@"---------%d", person.age);
           };
Copy the code

RLPerson *__strong person;

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; RLPerson *__strong person; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, RLPerson *__strong _person, int flags=0) : person(_person) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

Copy operation to capture block variables under MRC

The __main_block_desc_0 structure of c++ source block is changed internally

1. Capture the Auto object

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RLBlock block;
        {
            RLPerson *person = [[RLPerson alloc] init];
            person.age = 10;
            block = [^{
                NSLog(@"---------%d", person.age);
            } copy];
        }
        NSLog(@"-- -- -- -- -- -");
    }
    return 0;
}

Copy the code

C + + source code

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
Copy the code

2. Copy the auto object

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RLBlock block;
        {
            RLPerson *person = [[RLPerson alloc] init];
            person.age = 10;
            block = [^{
                NSLog(@"---------%d", person.age);
            } copy];
        }
        NSLog(@"-- -- -- -- -- -");
    }
    return 0;
}

Copy the code

View the source code

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); void (*dispose)(struct __main_block_impl_0*); } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};Copy the code

3. Capture the copy operation of the auto basic data type

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        RLBlock block;
        {
            int age = 10;
            block = ^{
                NSLog(@"---------%d", age);
            };
        }
        
        NSLog(@"-- -- -- -- -- -");
        
    }
    return 0;
}

Copy the code

View the source code

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
Copy the code

As you can see, when the block accesses the auto object type, Desc uses two more copy and dispose methods, which call the _Block_object_assign function. The copy operation does a strong reference based on the modifier preceding the variable. Dispose calls the _Block_object_dispose function to release the device


summary

  • When the block internally accesses the auto variable of the object type
    • If the block is on the stack, there is no strong reference to the auto variable
    • If a block is copied to the heap: Copy calls _Block_object_assign to the internal copy function that uses the block. _Block_object_assign requires retained or weak references based on the auto modifier (_strong, _weak, and _unsafe_unretained)
    • If a block is removed from the heap: The internal dispose function that uses the block calls the _Block_object_dispose function. The _Block_object_dispose function automatically releases the referenced auto variable (release)