循环引用是iOS开发中经常遇到的问题,尤其对于新手来说是个头疼的问题。循环引用对App有潜在的危害,会使内存消耗过高,性能变差和Crash等,iOS常见的内存主要以下三种情况:
1.Delegate
@property (nonatomic, weak) id delegate;
2.NSTimer
NSTimer我们开发中会用到很多,比如下面一段代码
- (void)viewDidLoad {
[super viewDidLoad];
selector:@selector(doSomeThing)
userInfo:nil
repeats:YES];
}
- (void)doSomeThing {
}
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
}
这是典型的循环引用,因为timer会强引用self,而self又持有了timer,所有就造成了循环引用。那有人可能会说,我使用一个weak指针,比如
__weak typeof(self) weakSelf = self;
self.mytimer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakSelf selector:@selector(doSomeThing) userInfo:nil repeats:YES];
但是其实并没有用,因为不管是weakSelf还是strongSelf,最终在NSTimer内部都会重新生成一个新的指针指向self,这是一个强引用的指针,结果就会导致循环引用。那怎么解决呢?主要有如下三种方式:
- 使用类方法 -block方式
@interface NSTimer (XXBlocksSupport)
+ (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats;
@end
@implementation NSTimer (XXBlocksSupport)
+ (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return
[self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(xx_blockInvoke:)
userInfo:[block copy]
repeats:repeats];
}
+ (void)xx_blockInvoke:(NSTimer *)timer {
void (^block)() = timer.userinfo;
if
(block) {
block();
}
}
@end
- 使用weakProxy
生成一个临时对象,让Timer强用用这个临时对象,在这个临时对象中弱引用self
- 使用GCD timer
具体如何使用,我就不做具体的介绍,网上有很多可以参考。
3.1 Block
Block的循环引用,主要是发生在ViewController中持有了block,比如:
@property (nonatomic, copy) LFCallbackBlock callbackBlock;
同时在对callbackBlock进行赋值的时候又调用了ViewController的方法,比如:
self.callbackBlock = ^{
[self doSomething];
}];
__weak __typeof(self) weakSelf = self;
self.callbackBlock = ^{
[weakSelf doSomething];
}];
原因是使用MRC
管理
内存时,Block的内存管理需要区分是Global(全局)、Stack(栈)还是Heap(堆),而在使用了ARC之后,苹果自动会将所有原本应该放在栈中的Block全部放到堆中。全局的Block比较简单,凡是没有引用到Block作用域外面的
参数
的Block都会放到全局内存块中,在全局内存块的Block不用考虑内存管理问题。(放在全局内存块是为了在之后再次调用该Block时能快速反应,当然没有调用外部参数的Block根本不会出现内存管理问题)。
所以Block的内存管理出现问题的,绝大部分都是在堆内存中的Block出现了问题。默认情况下,Block初始化都是在栈上的,但可能随时被收回,通过将Block类型声明为copy类型,这样对Block赋值的时候,会进行copy操作,copy到堆上,如果里面有对self的引用,则会有一个强引用的指针指向self,就会发生循环引用,如果采用weakSelf,内部不会有强类型的指针,所以可以解决循环引用问题。
3.2 Block
不允许修改外部变量的值
Block不允许修改外部变量的值
,这里所说的外部变量的值,指的是栈中指针的内存地址。
__block
所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
4.其他内存问题
2 动画的repeat count无限大,而且也不主动停止动画,基本就等于无限循环了;
内存解决思路:
1 通过Instruments来查看leaks