Blind date app development to solve the problem of memory circular reference

Posted by RobbertvanOs on Wed, 22 Dec 2021 16:06:26 +0100

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);
    };
  1. When self wants to release itself, it needs to release the block first,
  2. To release the block, first release the self holding of the executive body block,
  3. 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

Topics: iOS xcode objective-c