循环引用
强引用:某个对象被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
当popTimerViewController时,MainViewController指向TimerViewController的强引用指针被销毁,TimerViewController和NSTimer之间相互强引用,造成循环引用内存泄漏。
解决方案一
NSTimer弱引用TimerViewController
__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 <Foundation/Foundation.h>
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
- 当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已经强引用这个控件。