美文网首页
RunLoop 应用场景(NSTimer)

RunLoop 应用场景(NSTimer)

作者: qinghan | 来源:发表于2021-05-09 11:04 被阅读0次

    NSTimer 循环引用

    我们首先来下面一段代码

     self.name = @"123";
        _timer = [NSTimer scheduledTimerWithTimeInterval:1. target:self selector:@selector(timerTask) userInfo:nil repeats:YES];
    - (void)timerTask
    {
        NSLog(@"%@",self.name);
    }
    

    很明显上面的代码会发生会发生循环引用self->timer->self,那么如何打破循环呢

    使用block

    苹果可能知道上面的api会出现问题,所以给我们提供了一个block api,然后我们通过__weak来打破循环

         __weak typeof(self) weakSelf = self;
        _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"%@",weakSelf.name);
        }];
    
    手动打破循环

    就是在我们退出页面后,手动把timer赋值为空,当然这种方式有局限性

        - (void)didMoveToParentViewController:(UIViewController *)parent
    {
        if (!parent) {
            [self.timer invalidate];
            self.timer = nil;
        }
    }
    
    通过其他Target来实现
    _targetNew = [NSObject new];
    class_addMethod([NSObject class], @selector(timerTask), class_getMethodImplementation([self class], @selector(timerTask)), "v@:");
     _timer = [NSTimer scheduledTimerWithTimeInterval:1. target:_targetNew selector:@selector(timerTask) userInfo:nil repeats:YES];
    - (void)timerTask
    {
       // NSLog(@"%@",self.name);
        NSLog(@"timerTask");
    }
    

    这个方式也有弊端,如果在timerTask方法里面要应用self相关的属性,需要在_targetNew实例里面也要动态添加属性,否则会崩溃。

    通过NSProxy来实现

    首先继承NSProxy,通过NSProxy来实现消息转发。

    @interface QHProxy : NSProxy
    
    @property (nonatomic,weak)id fowardTarget;
    
    @end
    
    #import "QHProxy.h"
    
    @implementation QHProxy
    
    //消息转发
    - (void)forwardInvocation:(NSInvocation *)invocation
    {
        [invocation invokeWithTarget:self.fowardTarget];
    }
    
    
    //方法签名
    - (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
    {
        return [self.fowardTarget methodSignatureForSelector:sel];
    }
    
    @end
    

    具体实现

     _proxy = [QHProxy alloc];
     _proxy.fowardTarget = self;
        
     _timer = [NSTimer scheduledTimerWithTimeInterval:1. target:_proxy selector:@selector(timerTask) userInfo:nil repeats:YES];
    - (void)timerTask
    {
        NSLog(@"%@",self.name);
        NSLog(@"timerTask");
    }
    
    应用场景
     dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"1");
            //方法1
            [self performSelector:@selector(testActions) withObject:nil];
            
            //方法2
           // [self performSelector:@selector(testActions) withObject:nil afterDelay:0];
            //方法3
           // [self performSelector:@selector(testActions) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
            //方法4
            // [self performSelector:@selector(testActions) onThread:[NSThread currentThread] withObject:nil waitUntilDone:NO];
            NSLog(@"3");
        });
    - (void)testActions
    {
        NSLog(@"2");
    }
    
    • 方法1打印顺序123
    • 方法2打印13
    • 方法3打印123
    • 方法4打印13
      第一种不难理解,当是第二种为什么不答应2,原因很简单
      方法解释.png
      这个方法会加到当前线程的runloop,但是子线程的runloop没有开启,需要手动开启才能运行

    同样
    [self performSelector:@selector(testActions) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
    这个方法的wait参数,如果是YES,会立即返回,如果为NO,则也会加到当前线程runloop

    解释1.png

    相关文章

      网友评论

          本文标题:RunLoop 应用场景(NSTimer)

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