Block reference cycle (ARC & amp; non-ARC), block reference cycle arc

Source: Internet
Author: User

Block reference cycle problem (ARC & non-ARC), block reference cycle arc

  

Block implementation principle
First explore the implementation principle of Block. Since Objective-C is a superset of C language, since the NSObject object in OC is actually realized by the struct + isa pointer of C language, then the internal implementation of Block is also estimated. Blog made a detailed study on the implementation mechanism of Block:

A look inside blocks: Episode 1
A look inside blocks: Episode 2
A look inside blocks: Episode 3
Although the implementation details looked like a headache, it was found that Block was similar to NSObject in OC, and it was also implemented with struct. This is the struct statement about Block in the Block_private.h header file analyzed by the LLVM project compiler-rt:


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. * /
};
We found that there is also an isa pointer in Block_layout, which is like the isa pointer in the NSObject internal implementation struct. Isa here may point to one of three types of Block:

_NSConcreteGlobalBlock: The global type Block, which has been determined by the compiler, is placed directly on the code segment __TEXT. The type printed directly in NSLog is __NSGlobalBlock__.
_NSConcreteStackBlock: Block allocated on the stack, that is __NSStackBlock__.
_NSConcreteMallocBlock: Block allocated on the heap, namely __NSMallocBlock__
Why are there so many kinds? First look at the global type Block, see examples:


void addBlock (NSMutableArray * array) {
  [array addObject: ^ {
    printf ("global block \ n");
  }];
}
 
void example () {
  NSMutableArray * array = [NSMutableArray array];
  addBlock (array);
  void (^ block) () = [array objectAtIndex: 0];
  block ();
}
Why is the block added to the array in addBlock a global block? Because it does not need any state of the runtime (Runtime) to change its behavior, it does not need to be placed on the heap or stack, it can be directly compiled in the code segment, just like a C function. This type of Block is no different between ARC and non-ARC.

This Block accesses the variable d outside the scope. In the implementation, this block will have one more member variable corresponding to this d. When assigning the block, the value of the d variable in the method exmpale will be copied to the member variable to achieve access.


void example () {
  int d = 5;
  void (^ block) () = ^ () {
      printf ("% d \ n", d);
  };
  block ();
}
What if you want to modify d? :


void example () {
  int d = 5;
  void (^ block) () = ^ () {
      d ++;
      printf ("% d \ n", d);
  };
  block ();
  printf ("% d \ n", d);
}
Since the implementation of the local variable d and this block are not in the same scope, only the value transfer is used during the call process, so it cannot be directly modified, but you need to add an identifier __block int d = 5 ;, then the block can achieve this Modification of local variables. If it is such a variable identified by the block, it is no longer a simple member variable in the Block implementation, but a block corresponding to a new structure. The essence of block is to introduce a new Block_byref {$ var_name} {$ index} structure, and the variables modified by the block keyword are put into this structure. In addition, the block structure can indirectly access the external variables of Block by introducing members of the pointer type Block_byref {$ var_name} {$ index}. In this way, the access to variables outside the block is changed from value transfer to reference, so that it has the ability to modify the content.

Normally, we use the block to generate it on the stack, and release it after leaving the scope of the stack. If a block is copied, the block will be copied to the heap and allocated, so that it is no longer restricted by the stack and can be used at will. E.g:


typedef void (^ TestBlock) ();
 
TestBlock getBlock () {
  char e = 'E';
  void (^ returnedBlock) () = ^ {
    printf ("% c \ n", e);
  };
  return returnedBlock;
}
 
void example () {
  TestBlock block = getBlock ();
  block ();
}
The returnedBlock declared and assigned in the function getBlock is initially allocated on the stack and belongs to NSStackBlock. If it is non-ARC, the returned NSStackBlock is actually destroyed, and it will crash when used in example () . In the case of ARC, the block returned by getBlock will be automatically copied to the heap, then the type of block is NSMallocBlock, which can be used in example (). To run normally under Non-ARC, it should be modified to:


TestBlock getBlock () {
  char e = 'E';
  void (^ returnedBlock) () = ^ {
    printf ("% c \ n", e);
  };
  return [[returnedBlock copy] autorelease];
}
Circular reference problem in Block
After so much, back to the circular reference problem of Block, because many of our actions will cause Block to copy, and when Block is copied, it will generate strong references to the objects used in the block (under ARC) or reference count plus (Under non-ARC).

If you encounter this situation:


@property (nonatomic, readwrite, copy) completionBlock completionBlock;

// ========================================
self.completionBlock = ^ {
        if (self.success) {
            self.success (self.responseData);
        }
    }
};
The object has a Block property. However, this Block property refers to other member variables of the object, so it will have a strong application to the variable itself. Then the variable itself and its own Block property form a circular reference. Under ARC, it needs to be modified as follows:


@property (nonatomic, readwrite, copy) completionBlock completionBlock;

// ========================================
__weak typeof (self) weakSelf = self;
self.completionBlock = ^ {
    if (weakSelf.success) {
        weakSelf.success (weakSelf.responseData);
    }
};
That is to generate a weak reference to its own object. If the unlucky project needs to support iOS4.3, use __unsafe_unretained instead of __weak. If it is a non-ARC environment, replace __weak with __block. In the non-ARC case, the meaning of the __block variable is to introduce a new structure member variable in the Block to point to this __block variable, then __block typeof (self) weakSelf = self; it means that the Block should not retain the self object This breaks the circular reference.


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.