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来调用方法
一图来解释:
从而打破引用循环
网友评论