IOS martial arts esoteric article summary

Writing in the front

I’m sure you’re familiar with Blocks and see them in your daily development. This article will explain the concept of block, BLCOK circular reference, block bottom three aspects

A possible secret Demo for this section

First, Block preliminary study

(1) block definition

An anonymous function with automatic variables (local variables) is called a block

(2) block classification

There are three main types of blocks

  • __NSGlobalBlock__: globalblock, stored in the global area

In this case, the block has no parameters and no return value, and belongs to the global block

  • __NSMallocBlock__: heap areablockBecause theBlocks are both functions and objects

The block accesses an external variable, the underlying copy of A, so it’s a heap block

  • __NSStackBlock__: the stack zoneblock

Where the local variable A is a stack block before processing (before no copy), and a heap block after processing (after copy). Currently, there are fewer and fewer stack blocks

In this case, you can pass__weakNo strong holding,blockIt stillThe stack area block

Conclusion:

  • blockStored directly in the global area
  • ifblockAccess external variables and performblockCorresponding copy, that iscopyoperation
    • If at this pointBlocks are strong references,blockStored in theThe heap area, i.e.,Heap area block
    • If at this pointblockthrough__weakBecomes a weak reference, thenblockStored in theThe stack area, i.e.,The stack area block

Block loop reference

① Analysis of circular reference

  • Normal release: refers to theA hold BWhenAcalldeallocMethods toBsendreleaseSignal,BreceivedreleaseThe signal, if at this pointBtheretainCount(that is, reference count) is0Is calledBthedeallocmethods

  • A circular reference:A and BThey hold each other, which leads toACan’t calldeallocApproach toBsendreleaseSignal, butBI can’t receive itreleaseSignal. SoA and BThey can’t be released at this point

② Solve circular references

Do the following two pieces of code have circular references?

  • Code one kind of hairA circular reference is generatedBecause, inblockInternally usedExternal variable name, resulting inBlock is holding selfAnd theSelf originally owned blockSo it leads toSelf and block hold each other.
  • Code 2.Acyclic reference, although external variables are used, butselfThey don’t holdanimationtheblockOnly, onlyanimationholdself, does not constitute mutual possession.

There are several common ways to resolve circular references:

  • Methods (1) :weak-strong-dance— Dance between strong and weak
  • (2) :__blockModify objects (note that inblockInternal needsemptyObject, andblockMust call)
  • Method 3: Pass objectsselfAs ablockIs provided toblockInternal use
  • Mode 4: UseNSProxy

1 Mode ①: weak-strong-dance

  • ifblockThere is no nesting insideblock, direct use__weakmodifiedselfCan be

At this timeweakSelfandselfPoint to the same pieceMemory spaceAnd the use of__weakDoes not lead toselfThe reference count changes can be printedweakSelfandselfPointer address, as well asselfTo verify, as shown below

  • ifblockNested insideblockNeed to be used at the same time__weak__strong

StrongSelf is a temporary variable in the scope of the block, which releases strongSelf when the internal block is finished executing

This is breaking self’s strong reference to a block, depending on the mediator pattern, and is auto-set to nil, auto-release

②.2 Mode ②: __block modifies variables

This approach also depends onThe mediator pattern, belong toHand releaseIs through__blockModify objects, mainly because__blockThe modified object can be changed

Notice that the block here has to be called, if it’s not called, vc is not null, it’s still a circular reference, self and block are not released.

②.3 Method ③: The object self is used as a parameter

It’s basically the objectselfAs a parameter, provided toblockFor internal use, there will be no reference counting problems

4 Mode 4: NSProxy virtual class

  • OC is a single-inheritance language, but it is based on the run-time mechanism, so NSProxy can be used to implement pseudo-multiple inheritance, filling the gap of multiple inheritance

  • NSProxy and NSObject are sibling classes, or virtual classes, that implement the protocol of NSObject

  • NSProxy is an abstract class that encapsulates message redirection. It is like a proxy, middleware, that can inherit it and rewrite the following two methods to implement message forwarding to another instance

    - (void)forwardInvocation:(NSInvocation *)invocation;
    - (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
    Copy the code

Usage scenarios

There are two scenarios for using NSProxy

  • implementationMultiple inheritancefunction
  • To solve theNSTimer&CADisplayLinkCreated forselfStrong reference problem, referenceYYKittheYYWeakProxy

The principle of circular reference solution is to replace self with a custom NSProxy class object and use methods to implement message forwarding

The following is an implementation of the NSProxy subclass and the scenario in which it is used

  • Custom oneNSProxyA subclass ofCJLProxy

  • The customTCJCatClasses andTCJDogclass

  • throughTCJProxyRealize multiple inheritance function

  • throughTCJProxyResolve timerselfThe strong reference problem of

(2). 5

There are basically two solutions to looping applications, self -> block -> self for example

  • breakselfrightblockA strong reference to theblockProperty modifier is usedweakBut this will lead toblockIt is also released as soon as it is created, so breaking strong references from here won’t work
  • breakblockrightselfStrong reference, mainly isselfThe scope and ofblockScope communication, communication hasProxy, pass value, notification, pass parameterAnd other ways to solve the cycle, the common solutions are as follows:
    • weak-strong-dance
    • __block(blockThe inner object is empty and calledblock)
    • The objectselfAs ablockThe parameters of the
    • throughNSProxySubclass ofself

Iii. Underlying principle of Block

Mainly through clang, breakpoint debugging and other ways to analyze the Block bottom

(3). 1 block essence

  • defineblock.cfile

  • throughxcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc block.cThat will beblock.cCompiled intoblock.cpp, includingblockAt the bottom it is compiled into the following form

By simplification we know that the equivalent block is __main_block_IMPL_0, which is a function

  • To view__main_block_impl_0, it isA structureAt the same time, it can be explainedblockIs a__main_block_impl_0Type of object, that’s why, rightblockTo be able to% @Reasons for printing.

Summary: The essence of a block is an object, a function, or a structure. Since a block function has no name, it is also called an anonymous function or code block

blockthroughclangThe relationship between the compiled source code is shown below__blockFor example, modify a variable

③ 1.1 Why does block need to be called

The underlying block has a __main_block_IMPL_0 structure of type, created by its constructor of the same name, and the first passed block’s internal implementation code block, __main_block_func_0, is expressed as FP, which is then assigned to the FuncPtr property of the IMPL, The call is then made in main, which is why the block needs to be called. If not called, the code block implemented inside the block will not execute, which can be summarized as follows

  • Function declaration: that is,blockThe internal implementation declares a function__main_block_func_0
  • Perform specific function implementations: by callingblocktheFuncPtrPointer, callblockperform

③ 1.2 How does block obtain external variables

  • Define a variable and set it toblockIn the call
  • The underlying compilation looks like this

The ain __main_block_func_0 is a copy of the value, and if you do a++ inside a block implementation, it is problematic and will cause confusion in the compiler code that a is read-only.

Summary: When a block captures an external variable, it automatically generates the same internal property to hold

③ 1.3 the principle of __block

  • rightaAdd a__blockAnd then inblockIn theafor++operation

The underlying compilation is as follows

  • mainIn theaIs encapsulated by external variablesobject
  • __main_block_impl_0,Object aThe address of the&aTo the constructor
  • in__main_block_func_0Within theaThe process isPointer to the copy, the object created at this timeaWith the object passed inaPoint to theSame memory space

Conclusion:

  • External variablesthrough__blockgenerate__Block_byref_a_0The structure of the body
  • The structure of the bodyUsed toSaves the pointer and value of the original variable
  • To generate a variableThe pointer address of a structure objectPassed to theblockAnd then inblockYou can manipulate external variables internally

The __block and non-__block modifier local variables above produce two different copies

  • The __block modification:Copy the valueDeep copy, just copy the value, and the copied value cannot be changed, pointing to a different memory space, in the case of ordinary variablesaisCopy the value
  • __block modification:Pointer to the copyShallow copy.Generated objectPoint to theSame memory space, through the case__blockModified variableaisPointer to the copy

③.2 Analyze the location of block source code

  • Through theblockAt the break point, analyze the runtimeblock, open assembly

  • addobjc_retainBlockSymbol breakpoint, found will go to_Block_copy

  • add_Block_copySymbol breakpoint, run to break, atlibsystem_blocks.dylibIn the source

You can download the latest version from Apple’s open source websitelibclosure-78Source code, by viewing_Block_copySource code implementation, foundblockThe real type at the bottom isBlock_layout

3 Block type: Block_layout

To viewBlock_layoutThe definition of a type, yesA structure

  • Isa: Points to a class that indicates the block type

  • Flags: The flags identifier represents the additional information of some blocks in bits, similar to the bit fields in ISA. There are several types of flags. Focus on BLOCK_HAS_COPY_DISPOSE and block_has_signature. BLOCK_HAS_COPY_DISPOSE determines whether block_descriptor_2.block_has_signature is present Determines whether Block_descriptor_3 is present

    • 1 a:BLOCK_DEALLOCATING, release flag, – commonBLOCK_NEEDS_FREEwithThe operation is passed in togetherFlagsTo inform theblockCan release
    • 16:BLOCK_REFCOUNT_MASK, the storageThe value of the reference count; Is an optional parameter
    • 24th:BLOCK_NEEDS_FREE, 16Indicates whether or not it is validThe program decides based on itWhether to increase or decrease reference count bitsValue;
    • 25:BLOCK_HAS_COPY_DISPOSE, whether or notCopy helper function(a copy helper function);
    • 26:BLOCK_HAS_CTOR, whether or notblockDestructor;
    • 27th:BLOCK_IS_GC, indicating whether there is garbage collection; //OS X
    • 28:BLOCK_IS_GLOBALIs the flag globalblock;
    • 30th:BLOCK_HAS_SIGNATURE, andBLOCK_USE_STRETIn contrast, judge the presentblockWhether you have a signature. Used forruntimeTime dynamic call
  • Reserved: Reserved information. It can be understood that the reserved position is used to store information about internal variables of a block

  • Invoke: Is a function pointer to the block’s executing code

  • Descriptor: Additional information about a block, such as the number of variables to keep, the size of the block, and Pointers to auxiliary functions to copy or dispose. Have three kinds of

    • Block_descriptor_1Is choice
    • Block_descriptor_2Block_descriptor_3It’s all optional

About the abovedescriptorThe classification of can be reflected in its constructor, whereBlock_descriptor_2andBlock_descriptor_3Is throughBlock_descriptor_1Address, pastTranslation memoryTo get the

③.4 Block Memory changes

  • Interrupt point run, open assembly mode, go toobjc_retainBlock.blockThe breakpoint reads the registerx0For the time of,blockisGlobal block, i.e.,__NSGlobalBlock__type

  • Increase externalVariable aAnd, inblockprinting

At this time to readblockThe breakpoint reads the registerx0For the time of,blockisStack block__NSStackBlock__

  • Execute to symbol breakpointobjc_retainBlock,The stack area block

  • increase_Block_copySymbol breakpoint and break, directly at the lastretAdd breakpoint, readx0, discovered by_Block_copyAnd then, it becomes thetaHeap block, i.e.,__NSMallocBlock__Mainly becauseblockThe address has been changed toHeap block

Call the situation

  • This can also be verified using breakpoints

  • register read x0readx0forHeap block

  • Further down,register read x9readx9, orHeap block

  • To continue downregister read x11In this case, it refers to a piece of memory space for storage_block_invoke

  • Hold down thecontrol + step intoAnd into the_block_invoke, can be concluded is throughTranslation memoryTo get theblockInternal implementation

From the source code of the Block_layout structure mentioned above, it can be seen that there is an attribute called invoke, that is, the executor of the block, which is transferred to invoke from isa’s first address translation of 16 bytes, and then executes the call.

(3). 5

Go ahead, read the X0 register, look at the memory layout, and get the property descriptor Block_layout by translation 3*8, mainly to see if there is Block_descriptor_2 and Block_descriptor_3, Three of them have block’s signature

  • Register read x0, read register x0

  • Po 0x0000000280AD9Fb0 prints a block

  • x/8gx 0x0000000280ad9fb0, that is, printblockThe memory of

  • x/8gx 0x0000000100024010To see thedescriptorThe memory condition of the third one0x0000000100023396Said the signature

  • Check whether Block_descriptor_2 is present, that is, whether BLOCK_HAS_COPY_DISPOSE (copy helper function) of flags has a value

    • p/x 1<<25, i.e.,1 moved 25 to the left, whose hexadecimal is0x2000000
    • p 0x02000000 & 0x00000000c1000002, i.e.,BLOCK_HAS_COPY_DISPOSE & flagsIs equal to0, means noBlock_descriptor_2
  • Check whether Block_descriptor_3 is present

  • P /x 1<<30, that is, 1 moves 30 bits to the left

  • p 0x40000000 & 0x00000000c1000002, i.e.,BLOCK_HAS_SIGNATURE & flagsIf there is a value, there isBlock_descriptor_3

  • p (char *)0x000000010089f395– access toBlock_descriptor_3The properties in thesignatureThe signature

  • po [NSMethodSignature signatureWithObjCTypes:"v8@?0"], that is, print the signature

The signature part is described as follows

// No return value
return value: -------- -------- -------- --------
    type encoding (v) 'v'
    flags {}
    modifiers {}
    frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
    memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
    //encoding = (@), type @?
    type encoding (@) '@? '
    //@ is isObject,? IsBlock stands for isBlockObject
    flags {isObject, isBlock}
    modifiers {}
    frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
    // The offset is 8 bytes
    memory {offset = 0, size = 8}
Copy the code

The block signature information is similar to the method signature information. It mainly shows the block return value, parameters, and types.

③.6 Block copy analysis for three times

③ 6.1 _Block_copy source code analysis

Enter the_Block_copySource,blockfromThe stack areaCopy toThe heap area

  • If you need to release, release it directly
    • blockThe reference count ofruntimeDealt with, is by their own management
    • There might be a question here — why is the reference count +2 instead of +1?
    • becauseflagsThe first position of theRelease tag
  • If it isglobalBlock, is not requiredcopy, return directly
  • Instead, there are only two cases:The stack area block or Heap area blockBecause ofHeap area blockNeed to apply for space, there is no relevant code to apply for space, so it can only beThe stack area block
    • throughmallocAllocates memory space for receivingblock
    • throughmemmovewillblockCopy the file to the newly obtained memory
    • Set up theblockThe object is of typeHeap area block, i.e.,result->isa = _NSConcreteMallocBlock

(3). 6.2 _Block_object_assign analysis

Want to analyzeblocktheThree layers of the copyFirst, we need to know what kinds of external variables are in__block CPP fileIs passed when a function is declared__main_block_desc_0_DATAStruct, it’s going to be called from inside__main_block_copy_0The function,__main_block_copy_0It will call_Block_object_assign.

rightBlock modificationOne of the most used isBLOCK_FIELD_IS_OBJECTandBLOCK_FIELD_IS_BYREF

The _Block_object_assign method is called when external variables are copied in the underlying compiled code

Enter the_Block_object_assignThe source code

  • 1. If it is a common object, submit itARCProcessing, andCopy object pointer, i.e.,Reference count +1, soExternal variables cannot be released
  • 2. If it isblockType is passed_Block_copyOperation,blockfromThe stack is copied to the heap
  • 3. If it is__blockModify the variable called_Block_byref_copyFunction, performMemory copyAnd routine treatment

At this point, the captured variable is__blockModification of theBLOCK_FIELD_IS_BYREFType, is called*dest = _Block_byref_copy(object);

  • 1. Forcibly convert the incoming object toBlock_byrefStructure type object, save a copy
  • 2. No external variablesCopy to the heap, you need toApplication memory, itsTo copy
  • If it has already been copied, it is processed and returned
  • Among themcopyandsrctheforwardingPointers arePointing to the same piece of memoryIs that why__blockModification of theObject has the ability to modifyThe reason why
  • (*src2->byref_keep)(copy, src)
    • (*src2->byref_keep)(copy, src)Follow it and you’ll find itBlock_byrefStructure comes whilebyref_keepisBlock_byrefThe firstfiveattribute

Code debugging

  • To define a__blockModification of theNSStringobject

  • The results of clang compilation are as follows

  • The compiled cj_name is more than the normal variable __Block_byref_id_object_copy_131 and __Block_byref_id_object_dispose_131

  • __Block_byref_cj_name_0I have a lot of structures__Block_byref_id_object_copyand__Block_byref_id_object_dispose

_Block_copy->_Block_byref_copy->_Block_object_assign, which corresponds to the above three-layer copy

So how did Block get cj_NAME?

  • 1, through the_Block_copyMethods,blockMake a copy to the heap
  • 2, through the_Block_object_assignMethod normal copy because__blockThe external variables of the modifier are at the bottomBlock_byrefThe structure of the body
  • 3, find the external variable also has an object, frombrefTo retrieve the corresponding objectcj_nameCopy toblockSpace, can be used (the same space can be used, different can not be used). Finally, through the memory translation is obtainedcj_nameFor the time of,cj_nameAnd the outside worldcj_nameIs the same memory space (from_Block_object_assignIn the method*dest = object;See)

To summarize, the three-layer copy of a block refers to the following three layers:

  • [Layer 1] Through_Block_copyImplement a copy of the object itself, from the stack to the heap
  • [Second layer] Pass_Block_byref_copyMethod to copy the object toBlock_byrefStructural type
  • [Third layer] call_Block_object_assignMethods,__blockModifies a copy of the current variable

Note: Only__blockModified object,blockthecopyThere are three layers only

(3). 6.3 _Block_object_dispose analysis

in__Block_byref_id_object_dispose_131That’s called in the implementation_Block_object_disposeLet’s see_Block_object_disposeThe underlying implementation of

From the source code, we can know that _Block_object_dispose is a release operation, and different release operations are carried out through blocks of different partitions. And _Block_object_assign does the retain operation.

  • Enter the_Block_byref_releaseSource code, the main isRelease and destroy objects and variables
    • Do nothing if it is a free object (auto-free)
    • If it is__blockModification,Point the pointer back to the original area and free it with free

(3). 6.4 summary

  • blockIs the essence of__main_block_impl_0Structure object, so it can be used% @print
  • blockThe statement just putsblockThe implementation is saved, and the specific function implementation is requiredTo call
  • blockWhen an external variable is capturedblockStructure experienceAutomatically generate a propertyTo save the variable
  • __blockThe modified property generates the response structure underneath, holds a pointer to the original variable, and passes onePointer address to block
  • blockThere are three copies in:Copies of the block,Copy the memory address of the captured variable,Copy the object

BlockThe three layerscopyThe flow is shown in the figure below

Write in the back

Study harmoniously without being impatient. I’m still me, a different color of fireworks.