美文网首页
iOS NSTimer 的全部应用(二)Block解决循环引用

iOS NSTimer 的全部应用(二)Block解决循环引用

作者: Smallwolf_JS | 来源:发表于2018-08-20 14:42 被阅读17次

    第一种方式:使用block解决
    由于计时器会保留其目标对象,使用计时器时很容易引起循环引用,如下代码所示:

    @interface XXClass : NSObject
    - (void)start;
    - (void)stop;
    @end
    
    @implementation XXClass {
        NSTimer *timer;
    }
    
    - (id)init {
        return [super init];
    }
    
    - (void)dealloc {
        [timer]
    }
    
    - (void)stop {
        [timer invalidate];
        timer = nil;
    }
    
    - (void)start {
        timer = [NSTimerscheduledTimerWithTimeInterval:5.0 
                                                target:self  
                                              selector:selector(doSomething) 
                                              userInfo:nil 
                                               repeats:YES];
    }
    
    - (void)doSomething {
        //doSomething
    }
    
    @end
    

    大多数开发者可能都会这样来实现定时器。创建定时器的时候,由于目标对象是self,所以要保留此实例。然而,因为定时器是用实例变量存放的,所以实例也保留了定时器,这就造成了循环引用。除非调用stop方法,或者系统回收实例,才能打破循环引用,如果无法确保stop一定被调用,就极易造成内存泄露。
    当指向XXClass实例的最后一个外部引用移走之后,该实例仍然会继续存活,因为定时器还保留着它。而定时器对象也不可能被系统释放,因为实例中还有一个强引用正在指向它。这种内存泄露是很严重的,如果定时器每次轮训都执行一些下载工作,常常会更容易导致其他内存泄露问题。
    这个问题可以通过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
    

    定时器现在的target是NSTimer类对象,这是个单例,此处依然有循环引用,然后类对象无需回收,所以不用担心。
    这套代码并不能解决问题,例如:

    - (void)start {
        timer = [NSTimer xx_scheduledTimerWithTimeInterval:.5
                                                     block:^{
                                                     [self doSomething];
                                                            }
                                                   repeats:YES];
    }
    

    这段代码里还是有循环引用,因为Block捕获了self变量。此处只要改用weak引用,即可打破循环引用。

    • (void)start {
      __weak XXClass *weakSelf = self;
      timer = [NSTimer xx_scheduledTimerWithTimeInterval:.5
      block:^{
      XXClass *strongSelf = weakSelf;
      [strongSelf doSomething];
      }
      repeats:YES];
      }
      先定义了一个弱引用,令其指向self,然后使块捕获这个引用,而不直接去捕获普通的self变量。也就是说,self不会为计时器所保留。当块开始执行时,立刻生成strong引用,以保证实例在执行期间持续存活。
      采用这种写法之后,如果外界指向XXClass实例的最后一个引用将其释放,则该实例就可为系统所回收了。
      来自文章:https://www.jianshu.com/p/1dbd7a228a22

    相关文章

      网友评论

          本文标题:iOS NSTimer 的全部应用(二)Block解决循环引用

          本文链接:https://www.haomeiwen.com/subject/sqhliftx.html