在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來調用方法
一圖來解釋:
從而打破引用循環