优化-UIRunLoop实战

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

2017-07-06  本文已影响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来调用方法
一图来解释:

从而打破引用循环

上一篇 下一篇

猜你喜欢

热点阅读