SDWebImage源码阅读(一)SDWebImageCompat

  SDWebImageCompat 是SDWebImage 的配置文件,里面利用条件编译对Apple 的各个平台进行了兼容。可以看到SDWebImage 支持当前的MAC/iOS/TV/WATCH 平台。

  对各个平台的兼容性对框架开发意义重大。当前我们只是针对iOS 平台进行阅读理解,但是我们应该知道,当我们在别的Apple 的平台开发时,我们依然可以亲切并熟练的使用SDWebImage。

  (1): 导入 #import <TargetConditionals.h>

  按英语字面意思可以翻译为“目标依赖”,这个文件里面全部都是宏定义,主要定义了Apple 各系统平台和各CPU类型相关的宏。主要用于开发的时候针对不同的开发环境做配置使用。SDWebImageCompat 里面TARGET_OS 开头的宏定义都来自这里:

 1     #define TARGET_OS_MAC               1
 2     #define TARGET_OS_WIN32             0
 3     #define TARGET_OS_UNIX              0
 4     #define TARGET_OS_OSX               0
 5     #define TARGET_OS_IPHONE            1
 6     #define TARGET_OS_IOS               1
 7     #define TARGET_OS_WATCH             0
 8     #define TARGET_OS_BRIDGE            0
 9     #define TARGET_OS_TV                0
10     #define TARGET_OS_SIMULATOR         1
11     #define TARGET_OS_EMBEDDED          0 
12     #define TARGET_IPHONE_SIMULATOR     TARGET_OS_SIMULATOR /* deprecated */
13     #define TARGET_OS_NANO              TARGET_OS_WATCH /* deprecated */ 

  (2): 条件编译 __OBJC_GC__

1 #ifdef __OBJC_GC__
2     #error SDWebImage does not support Objective-C Garbage Collection
3 #endif

  Objective-C 支持内存的垃圾回收机制(Garbage collection 简称:GC)。在Mac开发是支持的,但是在iOS 开发中使用MRC/ARC,是不支持GC 的。

iOS 5 之后开始支持ARC ,帮助开发者更好的管理内存,简化内存管理的难度并提高开发效率。

  SDWebImage 不支持GC,如果宏定义过 __OBJC_GC__,则表示是在支持GC 的开发环境,直接报错(#error)。

  当启动GC时,所有的retain、autorelease、release 和dealloc 方法都将被系统忽略。

  延伸: 

  __OBJC__ 的使用:在OC 项目工程的pch 文件中我们经常能见到 :

1 #ifdef __OBJC__
2 // 中间引入OC 的头文件
3 #endif

  这样的预编译,这是因为在一个OC 工程中,可能会包含.m/.mm/.c/.cpp 这4类编译文件,这4类文件都会引入pch 文件里的预编译头,在编译.c/.cpp 文件时,因为他们是C 语言的文件,它们的语法是不兼容OC的,所以预编译头中是不能包含OC 代码,但是pch 是两类源文件共用的,所以在pch 中,OC 的头文件用__OBJC__ 包含起来,表示__OBJC__ 内引入的头文件只被使用OC 语言的文件所使用,保证引用关系的清晰。

参考链接:https://segmentfault.com/q/1010000000192619

http://www.jianshu.com/p/f3f4ba69e931

http://www.cnblogs.com/tekkaman/archive/2013/02/25/2931984.html

https://zhidao.baidu.com/question/328947129.html

   (3): #define SD_MAC

1 // Apple's defines from TargetConditionals.h are a bit weird.
2 // Seems like TARGET_OS_MAC is always defined (on all platforms).
3 // To determine if we are running on OSX, we can only relly on TARGET_OS_IPHONE=0 and all the other platforms
4 #if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
5     #define SD_MAC 1
6 #else
7     #define SD_MAC 0
8 #endif

  苹果的TARGET 宏定义都来自 TargetConditionals.h 是有一点怪。

  TARGET_OS_MAC 好像总是定义在所有的平台中 MAC Iphone Watch TV,单纯的使用TARGET_OS_MAC 判断当前是不是MAC 平台是不可行的。

当前 TargetConditionals.h 中 TARGET_OS_MAC 的宏定义是 1:

1     #define TARGET_OS_MAC               1

  为确定当前是运行在 OSX 平台,我们只能除了TARGET_OS_MAC,其它所有平台的都是0。(!TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH),而这样判断的缺点也很明显,当Apple 出现新的平台时,就要对判断条件进行修改,增加新的平台为!。

  (4): #define SD_UIKIT

1 // iOS and tvOS are very similar, UIKit exists on both platforms
2 // Note: watchOS also has UIKit, but it's very limited
3 #if TARGET_OS_IOS || TARGET_OS_TV
4     #define SD_UIKIT 1
5 #else
6     #define SD_UIKIT 0
7 #endif

  iOS 和 tvOS 是非常像的,UIKit 存在于它们两个平台上。

  watchOS 也有UIKit,但是它是非常受限的。当开发平台是iOS 或者TV 的时候宏定义 SD_UIKIT 为真。

  SDWebImageCompat.h 下面当SD_UIKIT 为真的时候引入 <UIKit/UIKit.h>。

  (5): #define SD_IOS/#define SD_TV/#define SD_WATCH

 1 #if TARGET_OS_IOS
 2     #define SD_IOS 1
 3 #else
 4     #define SD_IOS 0
 5 #endif
 6 
 7 #if TARGET_OS_TV
 8     #define SD_TV 1
 9 #else
10     #define SD_TV 0
11 #endif
12 
13 #if TARGET_OS_WATCH
14     #define SD_WATCH 1
15 #else
16     #define SD_WATCH 0
17 #endif

  宏定义 SD_IOS/SD_TV/SD_WATCH 用于区分不同的开发平台。

  (6): #if SD_MAC / #else / #endif

 1 #if SD_MAC
 2     #import <AppKit/AppKit.h>
 3     #ifndef UIImage
 4         #define UIImage NSImage
 5     #endif
 6     #ifndef UIImageView
 7         #define UIImageView NSImageView
 8     #endif
 9     #ifndef UIView
10         #define UIView NSView
11     #endif
12 #else
13     #if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
14         #error SDWebImage doesn't support Deployment Target version < 5.0
15     #endif
16 
17     #if SD_UIKIT
18         #import <UIKit/UIKit.h>
19     #endif
20     #if SD_WATCH
21         #import <WatchKit/WatchKit.h>
22     #endif
23 #endif

  如果SD_MAC 为真,即在macOS 平台上开发,引入 <AppKit/AppKit.h> 并定义了三个宏 UIImage/UIImageView/UIView。 

 

  系统的宏 __IPHONE_OS_VERSION_MIN_REQUIRED 对应着Xcode deployment target 的系统版本,且__IPHONE_OS_VERSION_MIN_REQUIRED 的值随着设置的值而改变,始终与你设置的值保持一致。TARGETS -> General -> Deployment Info -> Deployment Target 即:

  

  默认是空白的。

  __IPHONE_OS_VERSION_MIN_REQUIRED 定义在 usr/include -> AvailabilityInternal.h 中:

1 #ifndef __IPHONE_OS_VERSION_MIN_REQUIRED
2     #ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
3         /* compiler sets __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ when -miphoneos-version-min is used */
4         #define __IPHONE_OS_VERSION_MIN_REQUIRED __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
5     #endif
6 #endif

 

   系统的宏 __IPHONE_OS_VERSION_MAX_ALLOWED 对应着Xcode Architecture Base SDK 的版本。TARGETS ->General -> 搜索(Architecture) -> Architectures -> Base SDK 即:

  

  默认是当前Xcode 里面所有的SDK 的最高版本的之前的版本。( 当前Xcode SDK 最高版本是10.2,所以Base SDK 是 Latest iOS(iOS 10.2) )

  __IPHONE_OS_VERSION_MAX_ALLOWED 定义在 usr/include -> AvailabilityInternal.h 中:

 1 #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
 2     /* make sure a default max version is set */
 3     #ifndef __IPHONE_OS_VERSION_MAX_ALLOWED
 4         #define __IPHONE_OS_VERSION_MAX_ALLOWED     __IPHONE_10_2
 5     #endif
 6     /* make sure a valid min is set */
 7     #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_2_0
 8         #undef __IPHONE_OS_VERSION_MIN_REQUIRED
 9         #define __IPHONE_OS_VERSION_MIN_REQUIRED    __IPHONE_2_0 
10     #endif
11 ...
12 ...

  __IPHONE_10_2 定义在 usr/include -> Availability.h 中:

 1 #define __IPHONE_2_0      20000
 2 #define __IPHONE_2_1      20100
 3 #define __IPHONE_2_2      20200
 4 #define __IPHONE_3_0      30000
 5 #define __IPHONE_3_1      30100
 6 #define __IPHONE_3_2      30200
 7 #define __IPHONE_4_0      40000
 8 #define __IPHONE_4_1      40100
 9 #define __IPHONE_4_2      40200
10 #define __IPHONE_4_3      40300
11 #define __IPHONE_5_0      50000
12 #define __IPHONE_5_1      50100
13 #define __IPHONE_6_0      60000
14 #define __IPHONE_6_1      60100
15 #define __IPHONE_7_0      70000
16 #define __IPHONE_7_1      70100
17 #define __IPHONE_8_0      80000
18 #define __IPHONE_8_1      80100
19 #define __IPHONE_8_2      80200
20 #define __IPHONE_8_3      80300
21 #define __IPHONE_8_4      80400
22 #define __IPHONE_9_0      90000
23 #define __IPHONE_9_1      90100
24 #define __IPHONE_9_2      90200
25 #define __IPHONE_9_3      90300
26 #define __IPHONE_10_0    100000
27 #define __IPHONE_10_1    100100
28 #define __IPHONE_10_2    100200
29 /* __IPHONE_NA is not defined to a value but is uses as a token by macros to indicate that the API is unavailable */

  它包括了所有的iOS 系统的值,最后一个为当前Xcode 包含的SDK的最高版本。( 由于Xcode 没有升级,所以暂时最高到 10.2 )

  三个宏分别对应的头文件的引用:

1 #import <Availability.h>
2 #import <AvailabilityMacros.h>
3 #import <AvailabilityInternal.h>

  __IPHONE_OS_VERSION_MIN_REQUIRED/ __IPHONE_OS_VERSION_MAX_ALLOWED 会随着主动设置的值的改变而改变,所以用这两个宏做API 的兼容,往往存在缺陷。

  对于仅仅是判断某个方法是否可以使用,用 + (BOOL)respondsToSelector:(SEL)aSelector 方法即可。

  如果是判断当前Device 的iOS 版本,可以使用UIDevice 类提供的方法进行判断。

  示例:

 1 - (BOOL)osVersionAbove:(NSString *)aVersion{
 2     BOOL res;
 3     NSString *currentVersion = [UIDevice currentDevice].systemVersion;
 4     NSComparisonResult cmpRes = [currentVersion compare:aVersion options:NSNumericSearch];
 5     if (cmpRes == NSOrderedSame || cmpRes == NSOrderedDescending) {
 6         res = YES;
 7     }else{
 8         res = NO;
 9     }
10     return res;
11 }

  

 1 #ifdef __IPHONE_7_0
 2     if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
 3         // 代码段
 4     }
 5 #endif
 6     
 7 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
 8     if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
 9         // 代码段
10     }
11 #endif

  当使用framework或者.a的时候需要注意:

  因为在编译framework或者.a的时候是依赖当时编译的环境决定的,如使用iOS6的sdk可以将framework或者.a顺利编译通过,但是在真正生成app的时候需要保证编译app时的sdk和编译framework或者.a的sdk是一致的,且同时需要加入运行时判断([[[UIDevice currentDevice] systemVersion] floatValue]那个)

  说明:

  1、如果是使用低版本sdk编译的(如iOS6),将最终的应用安装至高版本的设备上(iOS7的系统),此时应该不会出现问题,因为iOS7兼容iOS6,但是无法使用iOS7特有的新功能,因为代码被屏蔽了。

  2、如果是使用高版本sdk编译的(如iOS7),将最终的应用安装至低版本的设备上(iOS6的系统),此时如果不加入运行时判断就会出现问题(可能是crash)。因为应用本身是用iOS7编译的,并使用iOS7才有的新功能,而设备并不支持iOS7。

  简单来说, 这些编译期常量, 对运行时的环境判断完全无效, 它告诉编译器用哪一段代码来进行编译, 在那段代码里, 你仍然可以写运行到目标机器里会崩溃的代码, 从stackoverflow里拷两段示例代码出来:

 1     // MAX:
 2 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 50000
 3     //you can use iOS 5 APIs here because the SDK supports them
 4     //but the code may still crash if run on an iOS 4 device
 5 #else
 6     //this code can’t use iOS 5 APIs as the SDK version doesn’t support them
 7 #endif
 8     // MIN:
 9 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 50000
10     //minimum deployment target is 5.0, so it’s safe to use iOS 5-only code
11 #else
12     //you can use iOS5 APIs, but the code will need to be backwards
13     //compatible or it will crash when run on an iOS 4 device  (向后兼容(Backward Compatibility),又称作向下兼容(Downward Compatibility)。在计算机中指在一个程序或者类库更新到较新的版本后,用旧的版本程序创建的文档或系统仍能被正常操作或使用,或在旧版本的 类库的基础上开发的程序仍能正常编译运行的情况。)
14 #endif

  如果碰到这个场景, 需要使用UIAlertViewController(ios8+),我能用上述哪种写法呢? 答案是, 都不行.

  max的取值来自于你的sdk的版本, 比如现在我的是Xcode6.1, sdk版本是iOS 8.1, 那么这个宏的值永远是80100, 也就是说, 会直接使用UIAlertViewController, 如果这个app跑在了iOS 7上, 就挂了. 所以单纯这么写只解决了你的ios8的api跑到低版本的xCode的时候不会报编译错误.

  min的取值来自于你项目的deployment target, 即你的app愿意支持的最低版本. 从这个角度来说, 片段2写法只对做可移植的包的开发团队有用, 即不知道使用方会将deployment target设成几. 而没有这个需求的, 这种写法就浪费了, 即你既然设定了最低支持ios6, 那么还写一段代码来判断如果最低支持ios7的情况下应该怎么做干嘛呢?

  那么我上面的问题(即我想使用新的api,但app又要支持旧的ios版本)怎么解决? 组合!
  1. 我先判断编译环境, 以避免低版本SDK不认识高版本SDK的api, 造成编译错误(片段1)
  2. 在高版本SDK的条件内, 自行开始判断SDK版本, 或responedToSelector都可以,来判断是否使用高版本的api
  3. 在低版本条件内, 直接用低版本的api

  片段2的场景很明确了,只针对最低的sdk版本写代码,这样使用你这个库的用户不管把deployment target设成几,都是看不到错误的, 只是, 这就使得这种代码执行起来, 永远用的是最旧的API。

  SDWebImage 不支持iOS 5.0 以下的版本。

  SD_UIKIT 为真时引入 <UIKit/UIKit.h>。

  SD_WATCH 为真时引入 <WatchKit/WatchKit.h>。

参考链接:http://www.jianshu.com/p/9579b661d9f7

http://blog.csdn.net/kingsley_zhong/article/details/45919823

http://blog.csdn.net/xyxjn/article/details/40425895

原文链接:http://www.cnblogs.com/wfwenchao/articles/4861078.html?utm_source=tuicool&utm_medium=referral

   (七): NS_ENUM/NS_OPTIONS

1 #ifndef NS_ENUM
2 #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
3 #endif
4 
5 #ifndef NS_OPTIONS
6 #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
7 #endif

  Foundation 框架 -> NSObjCRuntime.h 里面对NS_ENUM/NS_OPTIONS 的定义:

 1 /* NS_ENUM supports the use of one or two arguments. The first argument is always the integer type used for the values of the enum. The second argument is an optional type name for the macro. When specifying a type name, you must precede the macro with 'typedef' like so:
 2  
 3 typedef NS_ENUM(NSInteger, NSComparisonResult) {
 4     ...
 5 };
 6  
 7 If you do not specify a type name, do not use 'typedef'. For example:
 8  
 9 NS_ENUM(NSInteger) {
10     ...
11 };
12 */
13 #define NS_ENUM(...) CF_ENUM(__VA_ARGS__)
14 #define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name)

  参考: NS_ENUM/NS_OPTIONS 的区别

  (八): OS_OBJECT_USE_OBJC

  OS_OBJECT_USE_OBJC  这个宏时SDK 6.0 之后有的,usr/include -> os -> object.h 里面对它的定义:

1 #ifndef OS_OBJECT_USE_OBJC
2 #define OS_OBJECT_USE_OBJC 1
3 #endif

  在 SDK 6.0 之前为假,可以用  (__IPHONE_OS_VERSION_MIN_REQUIRED < 60000)代替它。

 1 #if OS_OBJECT_USE_OBJC
 2     #undef SDDispatchQueueRelease
 3     #undef SDDispatchQueueSetterSementics
 4     #define SDDispatchQueueRelease(q)
 5     #define SDDispatchQueueSetterSementics strong
 6 #else
 7     #undef SDDispatchQueueRelease
 8     #undef SDDispatchQueueSetterSementics
 9     #define SDDispatchQueueRelease(q) (dispatch_release(q))
10     #define SDDispatchQueueSetterSementics assign
11 #endif

  这里主要是针对GCD 的,GCD 中的对象在6.0之前是不参与ARC的,而6.0之后 在ARC下使用GCD不用关心释放问题。

  对于最低sdk 版本 >= iOS 6.0来说,GCD 对象已经纳入了ARC 的管理范围,我们就不需要再手工调用 dispatch_release 了,否则的话,在sdk < 6.0的时候,即使我们开启了ARC ,这个宏OS_OBJECT_USE_OBJC 也是没有的,也就是说这个时候,GCD对象还必须得自己管理。

   如果你开发的项目最低目标低于 iOS 6.0 or Mac OS X 10.8,你应该自己管理GCD对象,使用(dispatch_retain ,dispatch_release),ARC 并不会去管理它们。

   如果你开发的项目最低目标是 iOS 6.0 or Mac OS X 10.8 或者更高的,ARC 已经能够管理GCD 对象了,这时候,GCD 对象就如同普通的OC 对象一样,不应该使用dispatch_retain , ordispatch_release 。 

参考链接: http://blog.csdn.net/chenyong05314/article/details/18863417

http://www.cnblogs.com/xzoscar/p/5145740.html

  (九): API

1 extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
2 
3 typedef void(^SDWebImageNoParamsBlock)();
4 
5 extern NSString *const SDWebImageErrorDomain;

 static int64_t kAsyncTestTimeout = 5;

   extern 参考链接: http://baike.baidu.com/link?url=rRSL-MltD7ZM0kBuf2D_BxTxs8FME1xxpVXRWswLMG2AN3Y6HuetZ82gr9MRL7w334heuATqYaJiDLH-Xav0EK

  extern 可置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量或函数时,在其它模块中寻找其定义。另外,extern 也可用来进行链接指定。

  (十): dispatch_main_async_safe

1 #ifndef dispatch_main_async_safe
2 #define dispatch_main_async_safe(block)\
3     if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
4         block();\
5     } else {\
6         dispatch_async(dispatch_get_main_queue(), block);\
7     }
8 #endif

  dispatch_queue_get_label(dispatch_queue_t _Nullable queue) 获取参数queue 队列的名字,如果队列没有名字,返回NULL。

  strcmp 字符串比较函数。

  宏 DISPATCH_CURRENT_QUEUE_LABEL 代表当前对列。

  dispatch_get_main_queue() 主队列。

  获取当前对列的名字和主对列的名字进行比较,如果相同表示当前就是主队列,直接执行block() , 如果当前不是主队列,使用

dispatch_async(dispatch_queue_t queue, dispatch_block_t block) 异步调用主队列执行block() 。

  使用技巧:

  • 第一,我们可以像这样在定义宏的时候使用换行,但需要添加 \ 操作符
  • 第二,如果当前线程已经是主线程了,那么在调用dispatch_async(dispatch_get_main_queue(), block)有可能会出现crash
  • 第三,如果当前线程是主线程,直接调用,如果不是,调用dispatch_async(dispatch_get_main_queue(), block)

   (十一): SDWebImageCompat.m

 1 #if !__has_feature(objc_arc)
 2     #error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
 3 #endif
 4 
 5 // inline http://baike.baidu.com/link?url=HCLg60IJfwa55q1pfvZeHgUQ2g_kmN_jHSFlYWtHmDP2VgCL-rPcyzJ7b34fnyCIpMTP7mJex3_K7M7Vf14Czq
 6 
 7 inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) {
 8     if (!image) {
 9         return nil;
10     }
11     
12 #if SD_MAC // 如果是MAC 直接返回image
13     return image;
14     
15 #elif SD_UIKIT || SD_WATCH // 如果不是MAC 是 iOS TV 或者 WATCH
16     
17     if ((image.images).count > 0) { // 如果image.images.count 大于0
18         NSMutableArray<UIImage *> *scaledImages = [NSMutableArray array];
19         
20         for (UIImage *tempImage in image.images) {
21             [scaledImages addObject:SDScaledImageForKey(key, tempImage)];
22         }
23         
24         return [UIImage animatedImageWithImages:scaledImages duration:image.duration]; // 给 image 添加动画并把image 返回
25     } else {
26         
27 #if SD_WATCH // 条件编译 如果image.images.count 不大于0,且是WATCH的时候
28         if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) {
29 #elif SD_UIKIT // 条件编译 如果image.images.count 不大于0,且是iOS 或者是TV 有UIKit.h 引入的时候
30             if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
31 #endif
32             
33                 CGFloat scale = 1;
34                 if (key.length >= 8) {
35                     NSRange range = [key rangeOfString:@"@2x."];
36                     if (range.location != NSNotFound) { // 如果是2倍图
37                         scale = 2.0;
38                     }
39                     
40                     range = [key rangeOfString:@"@3x."];
41                     if (range.location != NSNotFound) { // 如果是3倍图
42                         scale = 3.0;
43                     }
44                 }
45                 
46                 UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; // 创建scaledImage,并返回
47                 
48                 image = scaledImage;
49             }
50             return image;
51         }
52         
53 #endif
54     }
55     
56 NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain";

  UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) 是根据参数key 来调整图片的scale。

  !__has_feature(objc_arc) SDWebImage 仅仅支持ARC 。

  inline: C++关键字,在函数声明或定义中函数返回类型前加上关键字inline,即可以把函数指定为内联函数。关键字inline必须与函数定义放在一起才能使函数成为内联,仅仅将inline放在函数声明前面不起任何作用。inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。一般的,用户可以阅读函数的声明,但是看不到函数的定义。

   如果是在iOS/TV/WATCH 平台下开发:

  如果 image.images.count 大于0,即image 是动效图片,则对image.images 里的每张图片做递归处理,根据参数key 调整image.scale,并通过调用 + (nullable UIImage *)animatedImageWithImages:(NSArray<UIImage *> *)images duration:(NSTimeInterval)duration NS_AVAILABLE_IOS(5_0),返回一个调整过大小的新的动效图片。

  如果 image.images.count 等于0,即image 是普通图片, 根据参数key,调整图片的scale,并返回调整过图片大小的新的图片。

 End

参考链接:http://www.jianshu.com/p/1d2e4d822732

posted @ 2017-04-28 15:48  鳄鱼不怕牙医不怕  阅读(547)  评论(0编辑  收藏  举报