block

In this post we’ll look at objc’s blocks and answer the following interview questions:

  1. The internal implementation of block, what does the structure look like
  2. Is a block a class? What are the types
  3. An int variable is__blockThe difference between embellishment or not? Block variable interception
  4. Block is modifying NSMutableArray. Do you need to add it__block
  5. How does memory management work
  6. Can blocks be modified with strong
  7. Why is it used to solve circular references__strong,__weakmodified
  8. blockhappencopyThe timing
  9. BlockOf the access object typeautoVariable when, inARCandMRCWhat’s the difference

Before we answer all the questions we need to know a little bit about the block background. As follows:

  • How do I see the internal implementation of a Block, that is, what the Block looks like when converted to the real C/C ++ code behind it? And conversion format or principle.
  • About the scope of a variable

Objective-c to C++

Here I write an example of the testClass. m class with block code as follows

OC code:

@interface TestClass ()
@end

@implementation TestClass
- (void)testMethods {
    void (^blockA)(int a) = ^(int a) {
        NSLog(@"%d",a);
    };
    if (blockA) {
        blockA(1990);
    }
}
@end
Copy the code

At the bottom of testclass.cpp we find the following code after the above transformation

C + + code

// @interface TestClass () /* @end */ // @implementation TestClass struct __TestClass__testMethods_block_impl_0 { struct  __block_impl impl; struct __TestClass__testMethods_block_desc_0* Desc; __TestClass__testMethods_block_impl_0(void *fp, struct __TestClass__testMethods_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __TestClass__testMethods_block_func_0(struct __TestClass__testMethods_block_impl_0 *__cself, int a) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_wx_b8tcry0j24dbhr7zlzjq3v340000gn_T_TestClass_ee18d3_mi_0,a); } static struct __TestClass__testMethods_block_desc_0 { size_t reserved; size_t Block_size; } __TestClass__testMethods_block_desc_0_DATA = { 0, sizeof(struct __TestClass__testMethods_block_impl_0)}; static void _I_TestClass_testMethods(TestClass * self, SEL _cmd) { void (*blockA)(int a) = ((void (*)(int))&__TestClass__testMethods_block_impl_0((void *)__TestClass__testMethods_block_func_0, &__TestClass__testMethods_block_desc_0_DATA)); if (blockA) { ((void (*)(__block_impl *, int))((__block_impl *)blockA)->FuncPtr)((__block_impl *)blockA, 1990); }}Copy the code

The above code generation is done by:

Open the terminal, CD to the testclass. m folder, and run the following command

clang -rewrite-objc TestClass.m
Copy the code

The corresponding testClass.cpp file is automatically generated in the current folder

Note: If you are prompted that clang does not exist and needs to be installed, enter the following

Brew install clang-format or brew link clang-forma then type the following command to check whether clang-format --help is enabledCopy the code

The above code shows that a Block is actually a structure type

The underlying implementation will subscript the __ class name __ method name _block_IMPL_ (0 for this method or the 0th block of this class, if any, will be the first block and the second…).

Struct __ class name __ method name _block_impl_ subscriptCopy the code

About the scope of a variable

The types of parameter variables that may be used in c functions

  • The parameter types
  • Automatic variable (local variable)
  • Static variables (static local variables)
  • Static global variable
  • The global variable

Because of the special storage area, three of these variables can be called at any time in any state.

  • A static variable
  • Static global variable
  • The global variable

The other two, however, have their own corresponding scope, beyond the scope, will be destroyed.


1. The internal implementation of block, what is the structure like

Let’s go back to this question with the background above

The internal implementation of block is as follows:

struct __TestClass__testMethods_block_impl_0 { struct __block_impl impl; Struct __TestClass__testMethods_block_desc_0* Desc; struct __TestClass__testMethods_block_desc_0* Desc; //desc struct declaration // constructor // fp function pointer //desc static global variable initialized __main_block_desc_ struct instance pointer // flags block load information (reference count and type information), stored by bit. __TestClass__testMethods_block_impl_0(void *fp, struct __TestClass__testMethods_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; // The code inside the block to be called in the future: // *__cself is a pointer to the value of the block, which is equivalent to the value of the block itself. Self) //__cself is a pointer to the __TestClass__testMethods_block_impl_0 structure implementation // The Block structure is the __TestClass__testMethods_block_impl_0 structure. The Block value is the static void __TestClass__testMethods_block_func_0(struct) constructed from __TestClass__testMethods_block_impl_0 __TestClass__testMethods_block_impl_0 *__cself, int a) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_wx_b8tcry0j24dbhr7zlzjq3v340000gn_T_TestClass_9f58f7_mi_0,a); } static struct __TestClass__testMethods_block_desc_0 { size_t reserved; size_t Block_size; } __TestClass__testMethods_block_desc_0_DATA = { 0, sizeof(struct __TestClass__testMethods_block_impl_0)}; static void _I_TestClass_testMethods(TestClass * self, SEL _cmd) { void (*blockA)(int a) = ((void (*)(int))&__TestClass__testMethods_block_impl_0((void *)__TestClass__testMethods_block_func_0, &__TestClass__testMethods_block_desc_0_DATA)); if (blockA) { ((void (*)(__block_impl *, int))((__block_impl *)blockA)->FuncPtr)((__block_impl *)blockA, 1990); }}Copy the code

As you can see, __TestClass__testMethods_block_impl_0 has three parts

  • Impl function pointer points to__TestClass__testMethods_block_impl_0
struct __block_impl { void *isa; int Flags; int Reserved; Void *FuncPtr; void *FuncPtr; // function pointer};Copy the code
  • Desc pointing__TestClass__testMethods_block_impl_0Is used to describe additional information about the current block, including the size of the structure, etc.
static struct __TestClass__testMethods_block_desc_0 { size_t reserved; Size_t Block_size; } __TestClass__testMethods_block_desc_0_DATA = {0, sizeof(struct __TestClass__testMethods_block_impl_0)};Copy the code
  • __TestClass__testMethods_block_impl_0()Constructor, which is the concrete implementation of the block
  __TestClass__testMethods_block_impl_0(void *fp, struct __TestClass__testMethods_block_desc_0 *desc, int flags=0) {
  impl.isa = &_NSConcreteStackBlock;
  impl.Flags = flags;
  impl.FuncPtr = fp;
  Desc = desc;
  }
Copy the code

In this structure

  • The ISA pointer holds a pointer to an instance of the structure of the class to which it belongs.
  • struct __TestClass__testMethods_block_impl_0A structure equivalent to an Objective-C class object
  • _NSConcreteStackBlockIt’s a struct instance of a Block, that isA block is essentially an Objective-C object implementation of a closure

Do you understand the internal implementation of a block at this point? Do you remember what the structure looks like? In fact, looking at the tedious careful observation of the code will find it is relatively simple.

2. Is block a class? What types are there?

Block is also a class because it has an ISA pointer. The types of block.isa include

  • _NSConcreteGlobalBlock, like a global variable, is set in the program’s.data area
  • _NSConcreteStackBlock on the stack
  • _NSConcreteMallocBlock heap

This ISA is bitwise operable

3. An int variable is removed__blockThe difference between embellishment or not? Block variable interception

be__blockThe difference between embellishment or not

Here’s an example of code to answer this question:

__block int a = 10; int b = 20; PrintTwoIntBlock block = ^(){ a -= 10; printf("%d, %d\n",a,b); }; block(); //0 20 a += 20; b += 30; printf("%d, %d\n",a,b); //20 50 block(); / 10 20Copy the code

Int A is modified by __block. The reference to this variable is a pointer copy that is passed into the block structure as a constructor argument and copies the pointer reference to the variable.

Int b is not decorated by __block, which inside b is a copy of the value. So changing B inside the block does not affect changes to B outside the block.

Block variable interception

Let’s look at variable capture through the following code

blk_t blk;
{
    id array = [NSMutableArray new];
    blk = [^(id object){
        [array addObject:object];
        NSLog(@"array count = %ld",[array count]);
    } copy];
}
blk([NSObject new]);
blk([NSObject new]);
blk([NSObject new]);
Copy the code

The output to print

block_demo[28963:1629127] array count = 1
block_demo[28963:1629127] array count = 2
block_demo[28963:1629127] array count = 3
Copy the code

Let’s translate the above code into C++

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

In Objc, C constructs cannot contain variables that are modified by __strong, because the compiler does not know when C constructs should be initialized and discarded. But Objc’s runtime library is able to accurately timing when blocks are copied from the stack to the heap and blocks on the heap are discarded, This is implemented by the __TestClass__testMethods_block_copy_0 function and the __TestClass__testMethods_block_dispose_0 function

static void __TestClass__testMethods_block_copy_0(struct __TestClass__testMethods_block_impl_0*dst, struct __TestClass__testMethods_block_impl_0*src) {
    _Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}  
static void __TestClass__testMethods_block_dispose_0(struct __TestClass__testMethods_block_impl_0*src) {
    _Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
Copy the code
  • _Block_object_assignEquivalent to the retain operation, the object is assigned to a structure member variable of the object type.
  • _Block_object_disposeThat’s the release operation.

When will these two functions be called?

function Called time
__TestClass__testMethods_block_copy_0 Copy from stack to heap
__TestClass__testMethods_block_dispose_0 Blocks on the heap are discarded
When are blocks on the stack copied to the heap?
  • Call block’s copy function.
  • When a Block is returned as a function return value.
  • Assign a Block to an enclosing__strongModifier for a class of type ID or a member of type Block.
  • Cocoa framework methods that contain usingBlocks, or GCD apis that pass blocks.
When do blocks become obsolete?
  • Call the dispose function when the Block on the heap is released and no one else holds it.

That’s what the block captures


4.blockIn the modifiedNSMutableArray, whether to add__block

  • Such as modifiedNSMutableArrayIf the content is stored, there is no need to add__blockModified.
  • Such as modifiedNSMutableArrayObject itself, that must be added__blockModification.

5. How does memory management work?

The isa pointer in the Block constructor __TestClass__testMethods_block_impl_0 above points to &_NSConcretestackBlock, which indicates that the current Block is in the stack.

Block memory operation Storage domain/storage location Impact of the copy operation
_NSConcreteGlobalBlock The data area of the program Do nothing
_NSConcreteStackBlock The stack Copy from stack to heap
_NSConcreteMallocBlock The heap Reference count increment

The global Block:_NSConcreteGlobalBlock struct instance is set in the program’s data store, so it can be accessed by a pointer anywhere in the program.

  • Block syntax is used to describe global variables.
  • Block does not intercept automatic variables.

If only one of the above two conditions is met, a global Block can be generated

  • Stack Block:_NSConcreteStackBlockAfter a Block is generated, if the Block is not a global Block, it is a stack Block, and its lifetime is within the scope of the variable to which it belongs. If the Block variable and__blockOnce a variable is copied to the heap, it is no longer affected by the end of its scope because it becomes a heap Block.
  • Heap Block:_NSConcreteMallocBlockAfter the stack block is copied to the heap, the ISA member variable of the block structure becomes_NSConcreteMallocBlock.

6. Can block be modified with strong?

In ARC you can, because blocks in an ARC environment can only be in heap or global memory, and therefore do not involve copying from the stack to the heap.

Not in MRC, because you have to copy. If you execute copy with strong, it will crash. Strong is the key introduced in ARC. Using retain is equivalent to ignoring the block copy process.

7. Why is it used to solve circular references__strong,__weakModified?

First, because a block captures a variable and passes in self when the structure is constructed, the default reference relationship is created. Therefore, the operation object is usually __weak outside the block, and the automatic variable of the object type is __strong inside the block. So when the block is copied from the stack to the heap, The object will be held by the Block, but the holding chain is longer than this because we have added __weak to it, just to resolve the impact of Block delay destruction on the life cycle of the external object. Failing to do so can easily create circular references.

8. When does block copy occur?

In ARC, the compiler automatically copies blocks created on the stack to the heap memory, and does not copy blocks passed as arguments to methods or functions.

  • Call block’s copy function.
  • When a Block is returned as a function return value.
  • Assign a Block to an enclosing__strongModifier for a class of type ID or a member of type Block.
  • Cocoa framework methods that contain usingBlocks, or GCD apis that pass blocks.

9. What is the difference between ARC and MRC when Block accesses the auto variable of object type?

ARC will strongly reference this object, MRC will not

Sharing:Big factory iOS often ask interview questions (with answers)

Since the | address