What is a circular reference
ARC has been out for a long time. It is really convenient to automatically release memory, but it is not absolutely safe and will never cause memory leakage in the development and application of blind date app. An invisible killer that makes iOS objects unable to be released as expected is circular reference. Circular reference can be simply understood as that a refers to B, while B refers to A. both parties maintain a reference of the other party at the same time, resulting in that the reference count is not 0 at any time and cannot be released. If the current object is a ViewController, its dealloc cannot be called after disass or pop. After frequent push or present, the memory increases sharply, and then the app hangs.
The partitions in iOS memory include heap, stack and static area. Among them, the stack and static area are managed and recycled by the operating system, which will not cause circular reference. Mutual references in the heap cannot be recycled, which may cause circular references.
The essence of circular reference: multiple objects have strong references to each other, which cannot be cast and recycled by the system.
The solution to circular references is to change the strong reference to the weak reference.
Circular reference scene
Basic controller code:
#import "ViewController.h" ///The first two resolve circular references typedef void(^CPBlock)(void); ///Used for the third solution to circular references typedef void(^CPParamBlock)(ViewController *); @interface ViewController () ///The first two solve circular reference examples @property(nonatomic, copy) CPBlock block; ///Example of circular reference for the third solution @property(nonatomic, copy) CPParamBlock paramBlock; @property(nonatomic, copy) NSString *name; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.name = @"CPBlock"; } @end
Block circular reference example code
Block is commonly used by us, and we should pay most attention to circular reference. Here, let's take an example of using block:
//1. self holding block self.block = ^(void){ // block hold self NSLog(@"%@", self); // block holds the attribute name of self NSLog(@"%@", self.name); };
- When self wants to release itself, it needs to release the block first,
- To release the block, first release the self holding of the executive body block,
- Self's retaincount= 0, self. Retain count of block= 0, both will never be released, resulting in circular reference
Delegate
The delegate attribute is declared as follows:
@property (nonatomic, weak) id <TestDelegate> delegate;
If weak is changed to strong, it will cause circular reference
// self -> AViewController BViewController *bVc = [BViewController new]; bVc = self; [self.navigationController pushViewController: bVc animated:YES]; // If it's strong // bVc. Delegate = = = > aviewcontroller (i.e. reference count of A + 1) // AViewController itself refers to < bviewcontrollerdelegate > = = = > delegate reference count + 1 // Cause: aviewcontroller < ======> delegate, which is a circular reference
Solve circular application
Method 1 Weak reference method to solve the circular application code developed by blind date app__ weak
-(void)resolveRetainCycle_1{ __weak typeof(self) weakSelf = self; self.block = ^(void){ NSLog(@"%@", weakSelf); // If there are time-consuming operations or asynchronous operations in the block execution body, we also need to optimize the solution of circular reference // strongSelf is a local variable in the block executor. It is used within the scope of the block executor. When the block is released, strongSelf will also be released automatically __strong typeof(weakSelf)strongSelf = weakSelf; // This asynchronous task with a delay of 2 seconds simulates a time-consuming operation dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", strongSelf.name); }); }; self.block(); }
Method 2 Intercepting variable mode__ block
-(void)resolveRetainCycle_2{ // Intercepts the current ViewController // Need to release vc = nil manually; __block ViewController *vc = self; self.block = ^(void){ // This asynchronous task with a delay of 2 seconds simulates a time-consuming operation dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", vc.name); vc = nil; }); }; self.block(); }
Method III Communication value transmission mode
///Three methods to solve circular reference -(void)resolveRetainCycle_3{ // Pass self (actually the current controller: ViewController) as a temporary variable into the block execution body // Does not cause circular references self.paramBlock = ^(ViewController *vc){ // This asynchronous task with a delay of 2 seconds simulates a time-consuming operation dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", vc.name); }); }; self.paramBlock(self); }
In this way, blind date app development can solve the problem of circular reference.
Statement: This article is forwarded by cloudleopard technology from teacher Ma Rui's blog. If there is any infringement, please contact the author to delete it