iOS-循环引用

循环引用

强引用:某个对象被strong指针强引用,指针未置为nil对象不会被销毁。

弱引用:某个对象被weak指针弱引用,对象销毁weak置为nil。

只要一个对象没有被strong指针指向那么该对象就是nil。

循环引用的实质:多个对象之间有强引用,不能释放让系统回收。

typeof与typedef

typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型。可以理解为:我们根据typeof()括号里面的变量,自动识别变量类型并返回该类型。常用于循环引用中。

typedef:定义一种类型的别名,而不只是简单的宏替换。

场景

Delegate

ViewController强引用UITableview属性,如果UITableview的代理是strong类型那么就会造成ViewController强引用UITableview,UITableview强引用ViewController,双方引用计数不为0,无法释放造成内存泄漏

@property (nonatomic, weak, nullable) id <UITableViewDataSource> dataSource;
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;

NSTimer

@interface TimerViewController ()

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation TimerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}


- (void)timerRun {
    NSLog(@"%s", __func__);
}

- (void)dealloc
{
    [self.timer invalidate];
    NSLog(@"%s", __func__);
}
@end

iOS-循环引用_第1张图片

当popTimerViewController时,MainViewController指向TimerViewController的强引用指针被销毁,TimerViewController和NSTimer之间相互强引用,造成循环引用内存泄漏。

解决方案一

NSTimer弱引用TimerViewController

iOS-循环引用_第2张图片

__weak typeof(self) weakSelf = self;
    self.timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        [weakSelf timerRun];
    }];
  • 当popTimerViewController时,MainViewController指向TimerViewController的强引用指针被销毁
  • 无强引用指针指向TimerViewController,TimerViewController开始销毁,调用dealloc方法
  • NSTimer从RunLoop中移除,指向NSTimer的强引用指针销毁
  • 当TimerViewController被销毁,指向NSTimer的强引用指针销毁,NSTimer无强引用指针指向,NSTimer释放

解决方案二

设置中间代理 NSTimer不直接持有TimerViewController,而是强引用代理对象,代理对象弱引用TimerViewController。

#import 

NS_ASSUME_NONNULL_BEGIN

@interface TimerProxy : NSObject
  
@property (nonatomic, weak) id target;

+ (instancetype)proxyWithTarget:(id)target;

@end

NS_ASSUME_NONNULL_END
------------------------------------------------------------
#import "TimerProxy.h"

@implementation TimerProxy

+ (instancetype)proxyWithTarget:(id)target {
    TimerProxy *proxy = [[TimerProxy alloc] init];
    proxy.target = target;
    return proxy;
}
// 消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector {
    return self.target;
}

@end
--------------------------------------------------------------
#import "TimerViewController.h"
#import "TimerProxy.h"

@interface TimerViewController ()

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation TimerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.timer = [NSTimer timerWithTimeInterval:1.0 target:[TimerProxy proxyWithTarget:self] selector:@selector(timerRun) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}


- (void)timerRun {
    NSLog(@"%s", __func__);
}

- (void)dealloc
{
    [self.timer invalidate];
    NSLog(@"%s", __func__);
}

@end

iOS-循环引用_第3张图片

  • 当popTimerViewController时,MainViewController指向TimerViewController强引用指针销毁
  • TimerViewController开始销毁,调用dealloc方法,NSTimer从NSRunLoop移除,NSRunLoop指向NSTimer的强引用指针销毁
  • TimerVIewController销毁完毕,TimerViewController指向NSTimer强引用指针销毁,NSTimer释放,TimerProxy释放

Block

block外部的weakSelf是为了避免循环引用,而block中的strongSelf是为了防止weakSelf提前释放,使得self引用计数加一,在闭包结束之后局部的strongSelf释放,self引用计数还原。

为什么Xib控件属性用weak修饰

UIViewController强引用view,view强引用属性subViews 当新增一个控件会以强引用的方式加入到subViews中,如果在UIViewController中使用strong修饰控件,removeFromSuperView后控件看不见但不能释放内存,使用weak修饰能持有该控件因为view已经强引用这个控件。

你可能感兴趣的:(Objective-C,iOS)