iOS --NSTimer 会保留其目标对象 (34)

 计时器是一种很方便也很有用的对象, Foundation 框架中有一个类叫做 NSTimer ,开发者可以指定绝对的日期与时间,以便到时执行任务, 也可以指定执行任务的相对延迟时间,  

计时器还可以重复运行任务, 有个与之相关联的 '间隔值'可以用力爱指定任务的触发频率.  

计时器要和'运行循环'相关联,运行循环到时候会触发任务, 创建 NSTimer 时, 可以将其 '预先安排'在当前的运行循环中, 也可以先创建好, 然后由开发者自己来调度, 无论采用哪种方式, 只有把计时器放在运行循环里面, 它才能正常触发任务.

  例如: + (NSTimer *)scheduleTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats; 

用此方法创建出来的计时器, 会在指定的间隔时间之后执行任务, 也可以令其反复执行任务, 知道开发者稍后将其手动关闭为止, target 与 selector 参数表示计时器将在那个对象上调用哪个方法, 计时器会保留其目标对象, 等到自身'失效'时再释放此对象, 调用 invalidate 方法可令计时器失效, 执行完相关任务之后,一次性的计时器也会失效, 开发者若将计时器设置成重复执行模式, 那么必须自己调用 invalidata 方法, 才能令其失效.  

由于计时器会保留其目标对象, 所以反复执行任务通常会导致应用程序出问题, 也就是说, 设置成重复执行模式的那种计时器, 很容易引入'保留环', 

 #import@interface ClassA : NSObject

- (void)startPolling;

- (void)stopPolling;

@end

@implementation ClassA{

NSTimer * _pollTimer;

}

- (id)init{

return [super init];

}

- (void)dealloc{

[_pollTimer invalidate];

}

- (void)stopPolling{

[_pollTimer invalidate];

_pollTimer = nil;

}

- (void)startPolling{

_pollTimer = [NSTimer scheduleTimerWithTimeInterval:5.0 taeget:self selector:@selector(p_dePoll) userInfo:nil repeates:YES];

}

- (void)p_dePoll{

//////

}

如果创建了本类的实例, 并调用其 startPolling 方法, 那么如何呢? 创建计时器的时候, 由于目标对象是 self, 所以要保留此实例,然而.因为计时器是用实例变量存放的, 所以实例也保留计时器, 于是,就产生了 '保留环', 如果此环 能在某一刻打破, 那就不会出什么情况, 然而要想打破保留环, 只能改变实例变量 或 令计时器无效, 所于说, 要么调用 stopPolling ,要么令系统将此实例回收. 而且即便能满足此条件, 这种通过调用某方法来避免内存泄漏的做法, 也不是一个好主意, 另外, 如果想在系统回收本类实例的过程中令计时器无效, 那又会陷入死结, 因为在计时器尚且有效时, ClassA 实例的保留计数 绝不会降为 0, 因此系统也绝不会将其回收, 而现在又没人来调用 invalidate 方法, 所以计时器将已知处于有效状态.

当指向 ClassA 实例的最后一个外部引用移走之后, 该实例仍然会继续存活, 因为计时器还保留着它, 而计时器对象也不可能为系统所释放, 因为实例中还有一个 强引用 正在指向它,  于是,内存就泄漏了, 这种内存泄漏问题尤为严重, 因为计时器还将反复地执行轮询任务.

但从计时器本身入手, 很难解决这个问题, 这个问题可以用 '块'来解决.

scheduledTimerWithTimeInterval:1.0 repeats:(BOOL) block:^(NSTimer * _Nonnull timer) {

}

这个办法为何能解决 '保留环'问题呢? 这段代码将计时器所应执行的任务 封装成 '块' ,在调用计时器函数时, 只要计时器还有效, 就会一直保留着它, 然后在快里面采用 弱引用 调用方法. 使 块 捕获这个引用, 而不直接捕获普通的 self  变量, 也就是说, self 不会为计时器所保留.'=

采用这种写法之后, 如果外界指向 ClassA 实例的最后一个引用将其释放, 则该实例就可为系统所回收了, 回收过程中还会调用计时器的 invalidata 方法, 这样的话,计时器就不会再执行任务了, 此处使用 weak 引用还能令程序更加安全, 因为有时开发者可能在编写 dealloc 时忘了调用计时器的 invalidate 方法.从而导致计时器再次运行, 若发生这种情况, 则 块 里面的 weakSelf 会变成 nil.

总结:

NSTimer 对象会保留其目标, 知道计时器本身失效为止, 调用 invalidate 方法可 令计时器失效, 另外,一次性的计时器在触发任务完成之后也会失效.

反复执行任务的计时器 ,很容易引入 保留环 ,如果这种计时器的目标对象又保留了计时器本身, 那肯定会导致 保留环, 这种环状保留关系, 可能是直接发生的, 也可能是通过其他对象间接发生的.

可以扩充 NSTimer 的功能, 用 '块'来打破 保留环,不过.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容