RunLoop 应用场景(NSTimer)

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容