美文网首页优化-UIRunLoop实战
RunLoop优化Tableview多图加载性能及避免CFRun

RunLoop优化Tableview多图加载性能及避免CFRun

作者: 雁小军 | 来源:发表于2017-07-06 12:22 被阅读102次

    GitHub Demo
    在tableView快速滑动同时加载很多图片的时候经常会帧数很低 ,通常是因为runloop在一次循环中处理很多图片导致。解决办法就是让runloop的每次循环只处理一张图片:

        CFRunLoopRef _rl = CFRunLoopGetCurrent();
        CFRunLoopMode _mode = kCFRunLoopCommonModes;
        CFRunLoopObserverRef _observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            if (self.tasks.count == 0) {
                return;
            }
            Action action = self.tasks.firstObject;
            [self.tasks removeObjectAtIndex:0];
            action();
        });
        CFRunLoopAddObserver(_rl, _observer, _mode);
        CFRelease(_observer);
    
    

    会发现在这个viewController pop之后不会走dealloc,因为self强引用了CFRunLoopObserverRef,CFRunLoopObserverRef强引用handler,handler里又强引用了self,形成了一个retaincycle。而编译器并不会提示你。解决方法就是

        __weak typeof(self) weakSelf = self;
        CFRunLoopRef _rl = CFRunLoopGetCurrent();
        CFRunLoopMode _mode = kCFRunLoopCommonModes;
        CFRunLoopObserverRef _observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            if (weakSelf.tasks.count == 0) {
                return;
            }
            Action action = weakSelf.tasks.firstObject;
            [weakSelf.tasks removeObjectAtIndex:0];
            action();
        });
        CFRunLoopAddObserver(_rl, _observer, _mode);
        CFRelease(_observer);
    
    CFRunLoopRemoveObserver(_rl, _observer, _mode)
    

    但是第二种方法并没有一个好的调用时机


    当runloop处于kCFRunLoopDefaultMode模式下的时候,如果没有事件处理,runloop就处在休眠状态就不去处理图片了,这时候一般来个Timer驱动,如果使用NSTimer或CADisplayLink repeats时NSTimer不会invalidate,所以会保持对target的强引用,会导致循环引用,解决方法可以参考YYFPSLabel的处理方式:

        _link = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(tick:)];
        [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    

    使用另外一个对象YYWeakProxy作为CADisplayLink的target,YYWeakProxy同时持有一个self的弱引用。同时重写消息传递过程

    - (id)forwardingTargetForSelector:(SEL)selector {
        return _target;
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation {
        void *null = NULL;
        [invocation setReturnValue:&null];
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
        return [NSObject instanceMethodSignatureForSelector:@selector(init)];
    }
    

    这样当target调用方法的时候,会由于找不到方法而调用消息转发,_target就是弱引用的self,由self来调用方法
    一图来解释:

    从而打破引用循环

    相关文章

      网友评论

        本文标题:RunLoop优化Tableview多图加载性能及避免CFRun

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