SDWebImage源码学习(一) --- SDWebImage

2018-03-06  本文已影响46人  Maj_sunshine
第一步还是先学习SDWebImage中还不怎么熟悉的知识点

知识点1 --- #error

用来做显示编译出错内容。


知识点2 --- FOUNDATION_EXPORT

我们经常用#define 都可以用来定义常量,其实还可以使用FOUNDATION_EXPORT定义常量。

// .h文件中
FOUNDATION_EXPORT NSString *const SDWebImageErrorDomain

// .m文件中
NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain";

#define kMyConstantString @"Hello"

第二种是最简单的宏的写法,但是第一种方法在检测字符串的值是否相等的时候更快。对于第一种可以直接使用(stringInstance == MyFirstConstant)来比较,而define则使用的是这种.([stringInstance isEqualToString:MyFirstConstant])。第一种直接比较的是指针地址,而第二个则是一一比较字符串的每一个字符是否相等,第一种效率高.


知识点3 --- dispatch_queue_async_safe

源码

#ifndef dispatch_queue_async_safe
#define dispatch_queue_async_safe(queue, block)\
    if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(queue)) == 0) {\
        block();\
    } else {\
        dispatch_async(queue, block);\
    }
#endif

#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block) dispatch_queue_async_safe(dispatch_get_main_queue(), block)
#endif
// 前往登录
#define SWCheckLogin \
UIViewController *loginMainVC = [[HHRouter shared] matchController:SW_Login_LoginMain];\
loginMainVC.view.backgroundColor = kGetColor(255, 255, 255, 1.0); \
SWNavigationController *loginNav = [[SWNavigationController alloc] initWithRootViewController:loginMainVC]; \
[self presentViewController:loginNav animated:YES completion:nil]; \

这样在登录的时候,只需SWCheckLogin即可。

总结起来这代码意思就是获取的线程名称如果和主线程名称相同,则当前线程是主线程,直接调用block,如果不是,调用dispatch_async(dispatch_get_main_queue(), block)。


知识点4 --- NS_OPTIONS和NS_ENUM
typedef NS_OPTIONS(NSUInteger, UIControlEvents) {
    UIControlEventTouchDown                                         = 1 <<  0,      // on all touch downs
    UIControlEventTouchDownRepeat                                   = 1 <<  1,      // on multiple touchdowns (tap count > 1)
    UIControlEventTouchDragInside                                   = 1 <<  2,
    UIControlEventTouchDragOutside                                  = 1 <<  3,
    UIControlEventTouchDragEnter                                    = 1 <<  4,
    UIControlEventTouchDragExit                                     = 1 <<  5,
    UIControlEventTouchUpInside                                     = 1 <<  6,
    UIControlEventTouchUpOutside                                    = 1 <<  7,
    UIControlEventTouchCancel                                       = 1 <<  8,
}

事件的类型就是个NS_OPTIONS。
从上到下的值分别是二级制的
0000 0001 , // 1 * 2^0 = 1
0000 0010, // 1 * 2^1 = 2
0000 0100, // 1 * 2^2 = 4
0000 1000, // 1 * 2^3 = 8
0001 0000, // 1 * 2^4 = 16
0010 0000, // 1 * 2^5 = 32
0100 0000, // 1 * 2^6 = 64
1000 0000, // 1 * 2^7 = 128
比如要去这个枚举的下面三个枚举值

UIControlEventTouchUpInside| UIControlEventTouchUpOutside| UIControlEventTouchDown

这样我们就可以获取到这个枚举的确定唯一的枚举值0110 0001,值为128 + 64 + 1 = 193;位运算保证了这个数字的唯一性。以次来获取枚举组合。

typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
   UIViewAnimationCurveEaseInOut,         // slow at beginning and end
   UIViewAnimationCurveEaseIn,            // slow at beginning
   UIViewAnimationCurveEaseOut,           // slow at end
   UIViewAnimationCurveLinear,
};
从上到下依次为 0 1 2 3 ,普通枚举做不到两张值都获取, 因为组合枚举值做不到唯一性 。


知识点5- -- inline

1 解决函数调用效率问题。inline函数避免了普通函数的,在汇编时必须调用call的缺点:取消了函数的参数压栈,减少了调用的开销,提高效率.

2 用于替代宏,跟宏一样使用时进行了代码替换,这里是使用函数体替换了函数名,相比于宏,内联函数调用的时候会检查参数的类型,确保调用正确,而且内联函数也是函数,去除了预编译的过程。


知识点6 --- #if !__has_feature(objc_arc)

源码

#if !__has_feature(objc_arc)
#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif

意为如果当前环境不是ARC则编译报上述错,SDWebImage仅支持ARC环境。


知识点7 --- animatedImageWithImages:duration:

源码

 UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration];

我们提供一个图片数组,方法执行播放起来就像是一个个图片的帧动画,duration为方法的执行时间
动画的重复次数是由animationRepeatCount控制,0为一直重复。动画停止stopAnimating方法。


知识点8 --- NSMutableArray<UIImage *>

作用 : 用来标记数据中每个对象的类型,不标记时取出数组的数不初始化时都为id类型,标记后为UIImage 类型,可以使用这个类型的方法列表中的方法。

例: 项目中我们经常使用数组作容器放model,我们可以这样

@property (nonatomic, strong) NSMutableArray<SWMyFansOrLikesModel *> *dataArray

这样我们要调用这个数组的数据就不用

SWMyFansOrLikesModel *model = self.viewModel.dataArray[indexPath.row];
UIViewController *infoDeatil = [[HHRouter shared] matchController:[NSString stringWithFormat:@"%@?userId=%@",SW_UserInfo,model.userID]];

可以直接调用

UIViewController *infoDeatil = [[HHRouter shared] matchController:[NSString stringWithFormat:@"%@?userId=%@",SW_UserInfo,self.viewModel.dataArray[indexPath.row].userID]];

在tableView的didSelectRowAtIndexPath方法中去除model初始化的操作,这样子控制器就不必要导入model的头文件。


知识点8 --- 返回对应分辨率下面的图片

来看看源码中的实现

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
            CGFloat scale = 1;
            if (key.length >= 8) {
                NSRange range = [key rangeOfString:@"@2x."];
                    // “@2x.png”的长度为7
                if (range.location != NSNotFound) {
                    scale = 2.0;
                }
                
                range = [key rangeOfString:@"@3x."];
                if (range.location != NSNotFound) {
                    scale = 3.0;
                }
            }

            UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
            image = scaledImage;
        }

我们同在在调用图片的时候使用[UIImage imageNamed:@"xxx"],xcode会自动帮我们根据屏幕分辨率添加@2x @3x操作,进而查找图片。如果我们在你调用图片时候调用[UIImage imageNamed:@"xxx@2x"],xcode的会把xxx@2x 当名称 再根据分辨率添加@2x @3x操作 后,变成[UIImage imageNamed:@"xxx@2x@2x"]查找,找不到xcode会把图片当一倍图,导致了图片的拉伸。但是源码中的这个方法,[UIImage imageNamed:@"xxx@2x@2x"] ,SDWebImage会处理成 scale = 2.0;从而加载正确图片。

总结

SDWebImageCompat这个文件可以看成一个配置文件,写的是各平台兼容的宏,.m文件各个方法其实可以看成宏的替换内容。

上一篇 下一篇

猜你喜欢

热点阅读