SLImageView.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. //
  2. // SLImageView.m
  3. // WSLImageView
  4. //
  5. // Created by 王双龙 on 2018/10/26.
  6. // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea. All rights reserved.
  7. //
  8. #import "SLImageView.h"
  9. #include <mach/mach.h>
  10. #define SL_LOCK_VIEW(...) dispatch_semaphore_wait(view->_lock, DISPATCH_TIME_FOREVER); \
  11. __VA_ARGS__; \
  12. dispatch_semaphore_signal(view->_lock);
  13. #define SL_LOCK(...) dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER); \
  14. __VA_ARGS__; \
  15. dispatch_semaphore_signal(self->_lock);
  16. #define SL_BUFFER_SIZE (10 * 1024 * 1024) // 10MB (minimum memory buffer size)
  17. #define SL_CLAMP(_x_, _low_, _high_) (((_x_) > (_high_)) ? (_high_) : (((_x_) < (_low_)) ? (_low_) : (_x_)))
  18. #pragma mark - 弱引用对象
  19. //临时弱引用对象,解决循环引用的问题 引自 YYWeakProxy
  20. @interface SLWeakProxy : NSProxy <NSObject>
  21. @property (nullable, nonatomic, weak, readonly) id target;
  22. @end
  23. @implementation SLWeakProxy
  24. + (instancetype)proxyWithTarget:(id)target {
  25. return [[SLWeakProxy alloc] initWithTarget:target];
  26. }
  27. - (instancetype)initWithTarget:(id)target {
  28. _target = target;
  29. return self;
  30. }
  31. //将消息接收对象改为 _target
  32. - (id)forwardingTargetForSelector:(SEL)selector {
  33. return _target;
  34. }
  35. //self 对 target 是弱引用,一旦 target 被释放将调用下面两个方法,如果不实现的话会 crash
  36. - (void)forwardInvocation:(NSInvocation *)invocation {
  37. void *null = NULL;
  38. [invocation setReturnValue:&null];
  39. }
  40. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
  41. return [NSObject instanceMethodSignatureForSelector:@selector(init)];
  42. }
  43. - (BOOL)respondsToSelector:(SEL)aSelector {
  44. return [_target respondsToSelector:aSelector];
  45. }
  46. - (BOOL)isEqual:(id)object {
  47. return [_target isEqual:object];
  48. }
  49. - (NSUInteger)hash {
  50. return [_target hash];
  51. }
  52. - (Class)superclass {
  53. return [_target superclass];
  54. }
  55. - (Class)class {
  56. return [_target class];
  57. }
  58. - (BOOL)isKindOfClass:(Class)aClass {
  59. return [_target isKindOfClass:aClass];
  60. }
  61. - (BOOL)isMemberOfClass:(Class)aClass {
  62. return [_target isMemberOfClass:aClass];
  63. }
  64. - (BOOL)conformsToProtocol:(Protocol *)aProtocol {
  65. return [_target conformsToProtocol:aProtocol];
  66. }
  67. - (BOOL)isProxy {
  68. return YES;
  69. }
  70. - (NSString *)description {
  71. return [_target description];
  72. }
  73. - (NSString *)debugDescription {
  74. return [_target debugDescription];
  75. }
  76. @end
  77. #pragma mark - 帧动画ImageView
  78. @interface SLImageView () {
  79. @package
  80. CADisplayLink *_displayLink; /// 帧动画切换器
  81. NSTimeInterval _time; /// 上一帧展示完剩余的时间
  82. SLImage *_curAnimatedImage; /// 当前动画的image
  83. NSMutableDictionary *_buffer; ///< 帧缓冲池
  84. dispatch_semaphore_t _lock; ///< 给帧缓冲池_buffer加锁
  85. NSInteger _incrBufferCount; ///< 当前帧缓存池插入的帧数
  86. BOOL _bufferMiss; ///< 是否丢帧
  87. NSUInteger _maxBufferCount; ///< 最大的缓冲帧数量
  88. NSUInteger _totalFrameCount; ///< 总帧数
  89. SLImageFrame *_curFrame; ///< 当前展示的帧
  90. NSUInteger _curIndex; ///< 当前展示的帧索引
  91. dispatch_once_t _onceToken;
  92. NSOperationQueue *_requestQueue; ///< 连续获取某帧image的操作队列
  93. }
  94. /**
  95. 根据当前内存大小动态计算适合的缓存帧数
  96. */
  97. - (void)calcMaxBufferCount;
  98. @end
  99. #pragma mark - 解码操作
  100. //解码获取某帧image的线程操作
  101. @interface SLImageFrameDecodeOperation : NSOperation
  102. @property (nonatomic, weak) SLImageView *view;
  103. @property (nonatomic, assign) NSUInteger nextIndex; //解码下一帧
  104. @property (nonatomic, strong) SLImage *curImage;
  105. @end
  106. @implementation SLImageFrameDecodeOperation
  107. - (void)main {
  108. __strong SLImageView *view = _view;
  109. if (!view) return;
  110. if ([self isCancelled]) return;
  111. view->_incrBufferCount++;
  112. if (view->_incrBufferCount == 0) [view calcMaxBufferCount];
  113. if (view->_incrBufferCount > (NSInteger)view->_maxBufferCount) {
  114. view->_incrBufferCount = view->_maxBufferCount;
  115. }
  116. NSUInteger idx = _nextIndex;
  117. NSUInteger max = view->_incrBufferCount < 1 ? 1 : view->_incrBufferCount;
  118. NSUInteger total = view->_totalFrameCount;
  119. view = nil;
  120. for (int i = 0; i < max; i++, idx++) {
  121. @autoreleasepool {
  122. if (idx >= total) idx = 0;
  123. if ([self isCancelled]) break;
  124. __strong SLImageView *view = _view;
  125. if (!view) break;
  126. SL_LOCK_VIEW(BOOL miss = (view->_buffer[@(idx)] == nil));
  127. if (miss) {
  128. SLImageFrame *imageFrame = [_curImage imageFrameAtIndex:idx];
  129. if ([self isCancelled]) break;
  130. SL_LOCK_VIEW(view->_buffer[@(idx)] = imageFrame ? imageFrame : [NSNull null]);
  131. view = nil;
  132. }
  133. }
  134. }
  135. }
  136. @end
  137. @implementation SLImageView
  138. /**
  139. 重置动画
  140. */
  141. - (void)resetAnimated {
  142. dispatch_once(&_onceToken, ^{
  143. self->_lock = dispatch_semaphore_create(1);
  144. self->_buffer = [NSMutableDictionary new];
  145. self->_requestQueue = [[NSOperationQueue alloc] init];
  146. self->_requestQueue.maxConcurrentOperationCount = 1;
  147. self->_displayLink = [CADisplayLink displayLinkWithTarget:[SLWeakProxy proxyWithTarget:self] selector:@selector(playAnimationImage:)];
  148. [self->_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
  149. self->_displayLink.paused = YES;
  150. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  151. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
  152. });
  153. if (_curIndex != 0) {
  154. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  155. _curIndex = 0;
  156. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  157. }
  158. _displayLink.paused = !_currentIsPlaying;
  159. _time = 0;
  160. _curFrame = nil;
  161. _bufferMiss = NO;
  162. _incrBufferCount = 0;
  163. [_buffer removeAllObjects];
  164. [_requestQueue cancelAllOperations];
  165. }
  166. #pragma mark - Event Handle
  167. //切换图片帧
  168. - (void)playAnimationImage:(CADisplayLink *)displayLink {
  169. if (!_autoPlayAnimatedImage) {
  170. return;
  171. }
  172. NSMutableDictionary *buffer = _buffer;
  173. SLImageFrame *nextBufferedImage = nil;
  174. SLImageFrame *curBufferedImage = nil;
  175. BOOL bufferIsFull = NO;
  176. NSUInteger nextIndex = (_curIndex + 1) % _curAnimatedImage.frameCount;
  177. curBufferedImage = buffer[@(_curIndex)];
  178. nextBufferedImage = buffer[@(nextIndex)];
  179. NSTimeInterval imageDuration = 0;
  180. if (!_bufferMiss) {
  181. _time += _displayLink.duration;
  182. imageDuration = curBufferedImage == nil ? [_curAnimatedImage imageDurationAtIndex:_curIndex] : curBufferedImage.duration;
  183. if (_time < imageDuration) return;
  184. _time -= imageDuration;
  185. if (nextIndex == 0) {
  186. //一个循环完成
  187. }
  188. imageDuration = nextBufferedImage == nil ? [_curAnimatedImage imageDurationAtIndex:nextIndex] : nextBufferedImage.duration;
  189. if (_time > imageDuration) _time = imageDuration; // do not jump over frame
  190. }
  191. SL_LOCK(
  192. if (nextBufferedImage) {
  193. if ((int)_incrBufferCount < _totalFrameCount) {
  194. [buffer removeObjectForKey:@(nextIndex)];
  195. }
  196. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  197. _curIndex = nextIndex;
  198. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  199. _curFrame = nextBufferedImage == (id)[NSNull null] ? nil : nextBufferedImage;
  200. super.image = _curFrame.image == nil ? [UIImage new] :_curFrame.image ;
  201. nextIndex = (_curIndex + 1) % _totalFrameCount;
  202. _bufferMiss = NO;
  203. if (buffer.count == _totalFrameCount) {
  204. bufferIsFull = YES;
  205. }
  206. } else {
  207. _bufferMiss = YES;
  208. }
  209. )//LOCK
  210. if (!bufferIsFull && _requestQueue.operationCount == 0) {
  211. //异步串行队列去执行下一帧的解码任务
  212. SLImageFrameDecodeOperation *operation = [SLImageFrameDecodeOperation new];
  213. operation.view = self;
  214. operation.nextIndex = nextIndex;
  215. operation.curImage = _curAnimatedImage;
  216. [_requestQueue addOperation:operation];
  217. }
  218. }
  219. //收到内存警告,缓冲池里只保留下一帧
  220. - (void)didReceiveMemoryWarning:(NSNotification *)notification{
  221. [_requestQueue cancelAllOperations];
  222. [_requestQueue addOperationWithBlock: ^{
  223. self->_incrBufferCount = -60 - (int)(arc4random() % 120); // about 1~3 seconds to grow back..
  224. NSNumber *next = @((self->_curIndex + 1) % self->_totalFrameCount);
  225. SL_LOCK(
  226. NSArray * keys = self->_buffer.allKeys;
  227. for (NSNumber * key in keys) {
  228. if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
  229. [self->_buffer removeObjectForKey:key];
  230. }
  231. }
  232. )//LOCK
  233. }];
  234. }
  235. //进入后台,缓冲池里只保留下一帧
  236. - (void)didEnterBackground:(NSNotification *)notification{
  237. [_requestQueue cancelAllOperations];
  238. NSNumber *next = @((_curIndex + 1) % _totalFrameCount);
  239. SL_LOCK(
  240. NSArray * keys = _buffer.allKeys;
  241. for (NSNumber * key in keys) {
  242. if (![key isEqualToNumber:next]) { // keep the next frame for smoothly animation
  243. [_buffer removeObjectForKey:key];
  244. }
  245. }
  246. )//LOCK
  247. }
  248. #pragma mark - Help Methods
  249. // 根据当前内存大小动态计算适合的缓存帧数
  250. - (void)calcMaxBufferCount {
  251. int64_t bytes = (int64_t)[_curAnimatedImage imageFrameBytes];
  252. if (bytes == 0) bytes = 1024;
  253. int64_t total = [self memoryTotal];
  254. int64_t free = [self memoryFree];
  255. int64_t max = MIN(total * 0.2, free * 0.6);
  256. max = MAX(max, SL_BUFFER_SIZE);
  257. if (_maxBufferSize) max = max > _maxBufferSize ? _maxBufferSize : max;
  258. double maxBufferCount = (double)max / (double)bytes;
  259. maxBufferCount = SL_CLAMP(maxBufferCount, 1, 100);
  260. _maxBufferCount = maxBufferCount;
  261. }
  262. // 总共的内存大小
  263. - (int64_t)memoryTotal {
  264. int64_t mem = [[NSProcessInfo processInfo] physicalMemory];
  265. if (mem < -1) mem = -1;
  266. return mem;
  267. }
  268. // 空闲的内存大小
  269. - (int64_t)memoryFree {
  270. mach_port_t host_port = mach_host_self();
  271. mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
  272. vm_size_t page_size;
  273. vm_statistics_data_t vm_stat;
  274. kern_return_t kern;
  275. kern = host_page_size(host_port, &page_size);
  276. if (kern != KERN_SUCCESS) return -1;
  277. kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
  278. if (kern != KERN_SUCCESS) return -1;
  279. return vm_stat.free_count * page_size;
  280. }
  281. #pragma mark - Setter And Getter
  282. - (void)setImage:(SLImage *)image{
  283. if ([image isMemberOfClass:[UIImage class]]) {
  284. image = [SLImage imageWithData:UIImagePNGRepresentation(image)];
  285. }
  286. [self resetAnimated];
  287. _curAnimatedImage = (SLImage *)image;
  288. _totalFrameCount = _curAnimatedImage.frameCount;
  289. super.image = [_curAnimatedImage imageAtIndex:_curIndex];
  290. [self calcMaxBufferCount];
  291. [self didMoved];
  292. }
  293. - (void)setCurrentImageIndex:(NSUInteger)currentImageIndex{
  294. if (!_curAnimatedImage) return;
  295. if (currentImageIndex >= _curAnimatedImage.frameCount) return;
  296. if (_curIndex == currentImageIndex) return;
  297. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  298. dispatch_async(dispatch_get_main_queue(), ^{
  299. SL_LOCK(
  300. [self->_requestQueue cancelAllOperations];
  301. [self->_buffer removeAllObjects];
  302. [self willChangeValueForKey:@"currentAnimatedImageIndex"];
  303. self->_curIndex = currentImageIndex;
  304. [self didChangeValueForKey:@"currentAnimatedImageIndex"];
  305. self->_curFrame = [self->_curAnimatedImage imageFrameAtIndex:self->_curIndex];
  306. self->_time = 0;
  307. self->_bufferMiss = NO;
  308. super.image = [self->_curAnimatedImage imageAtIndex:self->_curIndex];
  309. )//LOCK
  310. });
  311. });
  312. }
  313. - (SLImage *)animatedImage {
  314. return _curAnimatedImage;
  315. }
  316. - (NSUInteger)currentImageIndex{
  317. return _curIndex;
  318. }
  319. - (SLImageType)imageType {
  320. return _curAnimatedImage.imageType;
  321. }
  322. #pragma mark - Overrice NSObject(NSKeyValueObservingCustomization)
  323. + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
  324. if ([key isEqualToString:@"currentAnimatedImageIndex"]) {
  325. return NO;
  326. }
  327. return [super automaticallyNotifiesObserversForKey:key];
  328. }
  329. #pragma mark - 重写系统方法
  330. - (instancetype)init {
  331. self = [super init];
  332. if (self) {
  333. _autoPlayAnimatedImage = YES;
  334. }
  335. return self;
  336. }
  337. - (instancetype)initWithFrame:(CGRect)frame {
  338. self = [super initWithFrame:frame];
  339. if (self) {
  340. _autoPlayAnimatedImage = YES;
  341. }
  342. return self;
  343. }
  344. /**
  345. 开始动画
  346. */
  347. - (void)startAnimating{
  348. if(_curAnimatedImage.frameCount > 1 && _totalFrameCount != 0){
  349. _displayLink.paused = NO;
  350. _currentIsPlaying = YES;
  351. }else{
  352. [self stopAnimating];
  353. _currentIsPlaying = NO;
  354. }
  355. }
  356. /**
  357. 关闭动画
  358. */
  359. - (void)stopAnimating{
  360. [super stopAnimating];
  361. [_requestQueue cancelAllOperations];
  362. _displayLink.paused = YES;
  363. _currentIsPlaying = NO;
  364. }
  365. - (void)dealloc {
  366. [self stopAnimating];
  367. [_displayLink invalidate];
  368. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  369. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
  370. }
  371. - (void)didMoveToWindow {
  372. [super didMoveToWindow];
  373. [self didMoved];
  374. }
  375. - (void)didMoveToSuperview {
  376. [super didMoveToSuperview];
  377. [self didMoved];
  378. }
  379. - (void)didMoved {
  380. if (self.autoPlayAnimatedImage) {
  381. if(self.superview && self.window) {
  382. [self startAnimating];
  383. } else {
  384. [self stopAnimating];
  385. }
  386. }
  387. }
  388. @end