| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- /*
- * This file is part of the SDWebImage package.
- * (c) Olivier Poitrey <rs@dailymotion.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- #import "SDImageFramePool.h"
- #import "SDInternalMacros.h"
- #import "objc/runtime.h"
- @interface SDImageFramePool ()
- @property (class, readonly) NSMapTable *providerFramePoolMap;
- @property (weak) id<SDAnimatedImageProvider> provider;
- @property (atomic) NSUInteger registerCount;
- @property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *frameBuffer;
- @property (nonatomic, strong) NSOperationQueue *fetchQueue;
- @end
- // Lock to ensure atomic behavior
- SD_LOCK_DECLARE_STATIC(_providerFramePoolMapLock);
- @implementation SDImageFramePool
- + (NSMapTable *)providerFramePoolMap {
- static NSMapTable *providerFramePoolMap;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- // Key use `hash` && `isEqual:`
- providerFramePoolMap = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality valueOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality];
- });
- return providerFramePoolMap;
- }
- #pragma mark - Life Cycle
- - (instancetype)init {
- self = [super init];
- if (self) {
- _frameBuffer = [NSMutableDictionary dictionary];
- _fetchQueue = [[NSOperationQueue alloc] init];
- _fetchQueue.maxConcurrentOperationCount = 1;
- _fetchQueue.name = @"com.hackemist.SDImageFramePool.fetchQueue";
- #if SD_UIKIT
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
- #endif
- }
- return self;
- }
- - (void)dealloc {
- #if SD_UIKIT
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
- #endif
- }
- - (void)didReceiveMemoryWarning:(NSNotification *)notification {
- [self removeAllFrames];
- }
- + (void)initialize {
- // Lock to ensure atomic behavior
- SD_LOCK_INIT(_providerFramePoolMapLock);
- }
- + (instancetype)registerProvider:(id<SDAnimatedImageProvider>)provider {
- // Lock to ensure atomic behavior
- SD_LOCK(_providerFramePoolMapLock);
- SDImageFramePool *framePool = [self.providerFramePoolMap objectForKey:provider];
- if (!framePool) {
- framePool = [[SDImageFramePool alloc] init];
- framePool.provider = provider;
- [self.providerFramePoolMap setObject:framePool forKey:provider];
- }
- framePool.registerCount += 1;
- SD_UNLOCK(_providerFramePoolMapLock);
- return framePool;
- }
- + (void)unregisterProvider:(id<SDAnimatedImageProvider>)provider {
- // Lock to ensure atomic behavior
- SD_LOCK(_providerFramePoolMapLock);
- SDImageFramePool *framePool = [self.providerFramePoolMap objectForKey:provider];
- if (!framePool) {
- SD_UNLOCK(_providerFramePoolMapLock);
- return;
- }
- framePool.registerCount -= 1;
- if (framePool.registerCount == 0) {
- [self.providerFramePoolMap removeObjectForKey:provider];
- }
- SD_UNLOCK(_providerFramePoolMapLock);
- }
- - (void)prefetchFrameAtIndex:(NSUInteger)index {
- @synchronized (self) {
- NSUInteger frameCount = self.frameBuffer.count;
- if (frameCount > self.maxBufferCount) {
- // Remove the frame buffer if need
- // TODO, use LRU or better algorithm to detect which frames to clear
- self.frameBuffer[@(index - 1)] = nil;
- self.frameBuffer[@(index + 1)] = nil;
- }
- }
-
- if (self.fetchQueue.operationCount == 0) {
- // Prefetch next frame in background queue
- id<SDAnimatedImageProvider> animatedProvider = self.provider;
- @weakify(self);
- NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
- @strongify(self);
- if (!self) {
- return;
- }
- UIImage *frame = [animatedProvider animatedImageFrameAtIndex:index];
-
- [self setFrame:frame atIndex:index];
- }];
- [self.fetchQueue addOperation:operation];
- }
- }
- - (void)setMaxConcurrentCount:(NSUInteger)maxConcurrentCount {
- self.fetchQueue.maxConcurrentOperationCount = maxConcurrentCount;
- }
- - (NSUInteger)currentFrameCount {
- NSUInteger frameCount = 0;
- @synchronized (self) {
- frameCount = self.frameBuffer.count;
- }
- return frameCount;
- }
- - (void)setFrame:(UIImage *)frame atIndex:(NSUInteger)index {
- @synchronized (self) {
- self.frameBuffer[@(index)] = frame;
- }
- }
- - (UIImage *)frameAtIndex:(NSUInteger)index {
- UIImage *frame;
- @synchronized (self) {
- frame = self.frameBuffer[@(index)];
- }
- return frame;
- }
- - (void)removeFrameAtIndex:(NSUInteger)index {
- @synchronized (self) {
- self.frameBuffer[@(index)] = nil;
- }
- }
- - (void)removeAllFrames {
- @synchronized (self) {
- [self.frameBuffer removeAllObjects];
- }
- }
- @end
|