面试合集iOS常用

RunLoop-线程保活

2020-11-01  本文已影响0人  越天高

01

AFNetWorking,它既是使用runloop来控制线程的生命周期,让子线程一直停留在内存当中,这样在子线程经常要做事情的比较好
我们可以自己开启一个子线程,然后手动打开里面的runloop,注意runloop的mode里面如果没有任何的source0,source1,timer,observer,他会立即销毁
port就相当于source1

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    MyThread *thread = [[MyThread alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];
    [thread start];
}
- (void)doSomeThing
{
    NSLog(@"doSomeThing");
    //这里相当于给他添加了一个source1
    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSRunLoopCommonModes];
    //开启runloop
    [[NSRunLoop currentRunLoop] run];
}

02


- (void)viewDidLoad
{
    [super viewDidLoad];
    self.myThread = [[MyThread alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];
    [self.myThread start];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  //让一些操作在我们自己线程中运行
    //waitUntilDone:如果为YES,后面的代码就会等前面的doOtherThing码执行完毕之后才执行后面的next
    [self performSelector:@selector(doOtherThing) onThread:self.myThread withObject:nil waitUntilDone:NO];
    NSLog(@"next ");
    NSLog(@"%@", [NSThread currentThread]);

}
- (void)doOtherThing
{//具体做的事情
    NSLog(@"doSomeThing");
    NSLog(@"%@", [NSThread currentThread]);

}
- (void)doSomeThing
{//为了线程保活
    NSLog(@"doSomeThing");
    
    NSLog(@"%@", [NSThread currentThread]);
    //这里相当于给他添加了一个source1
    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSRunLoopCommonModes];
    //开启runloop
    [[NSRunLoop currentRunLoop] run];


}

03

线程一致保活可能存在一定的细节问题,他会影响控制器的生命周期,导致控制器无法被销毁。 线程内部对控制器强引用,用block的方式执行任务,这样控制器是可以销毁,但是线程依旧没有销毁。我们要将线程控制的更加精准一点,可以吧runloop停掉,来销毁线程,他也有方法CFRunLoopStop(CFRunLoopGetCurrent)

04

因为run方法的调用还是不能直接销毁线程,
因为run方法开始的是无限的循环,所以是停不掉的,因为我们只能停止一次,所以创建的时候不要调用run。我们可以自己首先runMode:

- (IBAction)click:(UIButton *)sender {
//    NSLog(@"调用了系统的方法");
    [self performSelector:@selector(deallocMythread) onThread:self.myThread withObject:nil waitUntilDone:NO];
       
}

- (void)viewDidLoad
{
    [super viewDidLoad];
//    self.myThread = [[MyThread alloc] initWithTarget:self selector:@selector(doSomeThing) object:nil];
    self.isStop = NO;
    __weak typeof(self) weakSelf = self;
    self.myThread = [[MyThread alloc] initWithBlock:^{
        //为了线程保活
        NSLog(@"doSomeThing-beign");
        
        NSLog(@"%@", [NSThread currentThread]);
        //这里相当于给他添加了一个source1
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        //开启runloop ,使用run直接开始的是一个无法停止的无限循环,所以我们要自己开启一个
        while (!weakSelf.isStop)
        {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        NSLog(@"dosometing End");
    }];
    [self.myThread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
  //让一些操作在我们自己线程中运行
    //waitUntilDone:如果为YES,后面的代码就会等前面的doOtherThing码执行完毕之后才执行后面的next
    [self performSelector:@selector(doOtherThing) onThread:self.myThread withObject:nil waitUntilDone:NO];
    NSLog(@"next");
}
- (void)doOtherThing
{//具体做的事情
    NSLog(@"doOtherThing");
    NSLog(@"%@", [NSThread currentThread]);

}

-(void)deallocMythread
{
    //这个方法一定要在正确的线程中执行,
    self.isStop = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)dealloc
{
    //这里是主线程,所以不可以在这直接掉Stop,不然他停掉的是主线
    [self performSelector:@selector(deallocMythread) onThread:self.myThread withObject:nil waitUntilDone:NO];
    self.myThread = nil;   
}

答疑

给人感觉只使用标记就可以控制,是不对的,因为线程到runMode那里停到了,一致在那里休眠,就算你改了YES 代码还是卡在那里

05

为什么停止runloop的时候 用点击按钮可以,但是在dealloc里就不行?
因为在dealloc里面调用的时候,控制器正在销毁,我们这时候执行 [self performSelector:@selector(deallocMythread) onThread:self.myThread withObject:nil waitUntilDone:NO];来进行停止,最后那个参数为NO,说明主线程的销毁,不用等子线程执行完,就执行销毁操作。但是子线程还没有执行完,而且这个方法是由self来调用的,这样就找成了self已经销毁,我还用它发消息肯定有问题。因为runloop在处理主线程到子线程的调用,所以他那个代码会出坏内存访问(控制器坏了)
可以[self performSelector:@selector(deallocMythread) onThread:self.myThread withObject:nil waitUntilDone:YES];//代表子线程的代码执行完毕后,这个代码才往下执行,这样虽然解决了,坏内存访问,但是runloop没有停止成功。通过执行端点我们发现,他又到while里面执行代码了,因为我们转的weakSelf变成了null。当我们停掉了之前的runloop之后,self被销毁了,runloop就判断外面的循环是不是成立,因为weakSelf是一个弱指针,所以在他指向的对象销毁后,他会执行nil,这时候我们是取反,所以条件又成立,又会在开始一个runloop,可以加上(weakSelf && !weakSwlf.isStop)

线程保活06

使用线程的时候注意判断 if(!self.myThread)return;

上一篇 下一篇

猜你喜欢

热点阅读