美文网首页
关于上一篇"RunLoop 学习笔记"存在问

关于上一篇"RunLoop 学习笔记"存在问

作者: aLonelyRoot3 | 来源:发表于2016-07-25 19:01 被阅读142次

    上一篇中问题, 实现"常驻线程"的方案

    上一篇"RunLoop 学习笔记"中是这么介绍常驻线程, 以及对应实现方法的:

    即让子线程处于 "不消亡" 的状态, 一直在后台处理某些频发事件 / 等待其他线程发来消息

    • 在子线程监控网络状态
    • 在子线程开启一个定时器
    • 在子线程长期监控其他行为
    + (void)networkRequestThreadEntryPoint:(id)__unused object { 
        @autoreleasepool { 
            [[NSThread currentThread] setName:@"AFNetworking"];        
             NSRunLoop *runLoop = [NSRunLoop currentRunLoop];       
            [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; 
        }
    }
    

    摘自 AFNetworking 源代码, AFN这样做的原理在于子线程下默认不开启 RunLoop, 需要手动开启, 而 RunLoop 不断跑圈需要满足以下条件之一 :

    • RunLoop有事件源(输入源), 包含基于端口 (port) 的事件源 / custom 事件源等
    • RunLoop存在定时器

    因此, AFN为RunLoop的default模式增加了一个NSMachPort端口(实际上也可以是其他端口),也就相当于为RunLoop添加了事件源, 因此RunLoop可以不断的跑圈, 保证线程的不死状态
    顺便提一下, AFN保持一个常驻线程的原因, 第一是因为子线程默认不会开启RunLoop, 它会像一个C语言程序一样运行完所有代码后退出线程, 而网络请求是异步的, 这就可能会出现通过网络请求获取到数据之后, 线程已经退出, 无法执行请求成功/失败的代理方法, 因此AFN开启了一个RunLoop, 保活了线程

    这样做存在内存泄漏的问题

    经过研究 bestswifter 的文章 深入研究 Runloop 与线程保活 的相关思想与结论, 对以前提出的方案做改进, 并鸣谢该作者

    解决方案如下:

    - (void)memoryTest {
        for (int i = 0; i < 100000; ++i) {
            NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
            [thread start];
            [self performSelector:@selector(stopThread) onThread:thread withObject:nil waitUntilDone:YES];
        }
    }
    
    - (void)stopThread {
        CFRunLoopStop(CFRunLoopGetCurrent());
        NSThread *thread = [NSThread currentThread];
        [thread cancel];
    }
    
    - (void)run {
        @autoreleasepool {
            NSLog(@"current thread = %@", [NSThread currentThread]);
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            if (!self.emptyPort) {
                self.emptyPort = [NSMachPort port];
            }
            [runLoop addPort:self.emptyPort forMode:NSDefaultRunLoopMode];
            [runLoop runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];
        }
    }
    
    
    文/bestswifter(简书作者)
    原文链接:http://www.jianshu.com/p/10121d699c32
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
    

    关于 RunLoop 线程保活技术更深层次的理解, 请移步至 bestswifter 的文章 深入研究 Runloop 与线程保活

    相关文章

      网友评论

          本文标题:关于上一篇"RunLoop 学习笔记"存在问

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