循环引用

来源:互联网 发布:淘宝卖汽车配件怎么样 编辑:程序博客网 时间:2024/06/03 14:50
循环引用是iOS开发中经常遇到的问题,尤其对于新手来说是个头疼的问题。循环引用对App有潜在的危害,会使内存消耗过高,性能变差和Crash等,iOS常见的内存主要以下三种情况:
1.Delegate
代理协议是一个最典型的场景,需要你使用弱引用来避免循环引用。ARC时代,需要将代理声明为weak是一个即好又安全的做法:
@property (nonatomic, weak) id  delegate;
2.NSTimer
NSTimer我们开发中会用到很多,比如下面一段代码
- (void)viewDidLoad {
    [super viewDidLoad];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1target:self
                                            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];
}];
就会发生循环引用,因为:ViewController->强引用了callback->强引用了ViewController,解决方法也很简单:
__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,内部不会有强类型的指针,所以可以解决循环引用问题。
那是不是所有的block都会发生循环引用呢?其实不然,比如UIView的类方法Block动画,NSArray等的类的遍历方法,也都不会发生循环引用,因为当前控制器一般不会强引用一个类。
3.2 Block不允许修改外部变量的值
Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值。
4.其他内存问题
1 NSNotification addObserver之后,记得在dealloc里面添加remove;
2 动画的repeat count无限大,而且也不主动停止动画,基本就等于无限循环了;
3 forwardingTargetForSelector返回了self。
内存解决思路:
1 通过Instruments来查看leaks
2 集成Facebook开源的FBRetainCycleDetector
3 集成MLeaksFinderFBRetainCycleDetector 。
阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 保利海上五月花装修 成都五月花技工学校 五月花计算机专修学校 五月花职业技术学院 五月花开 五月里的鲜花 五菌合一新生面膜乳使用方法 纵横五代之武当掌门 沧浪小蛊 媚月蛊 五年级桂花雨 五方神尊 尘雨英少 昆明的雨赏析句子五句 五月雨 五月雨吉他谱 五月雨简谱 西边雨 五月艾草 五年级英语期终考试冲刺 五木宏 勇宏钢木家具 宏耐强化木地板 忽明忽暗by冷山就木 宏明泰铝包木门窗 八木美智香 青香木 木香记txt 桃奶木香奈 木香记石头水晋江 桃奶木香奈卡在墙里 木香记石头与水晋江 五朵云的功效与作用 五朵云作品集 五朵云图片 农家日常五朵云 种田.农家日常 农家日常 种田农家日常 种田.农家日常 五朵云 五朵山在哪里 镇平五朵山 冈拉梅朵 格桑梅朵