第一步还是先学习SDWebImage中还不怎么熟悉的知识点
知识点1 --- #error
用来做显示编译出错内容。
知识点2 --- FOUNDATION_EXPORT
我们经常用#define 都可以用来定义常量,其实还可以使用FOUNDATION_EXPORT定义常量。
- FOUNDATION_EXPORT源码中的使用
// .h文件中
FOUNDATION_EXPORT NSString *const SDWebImageErrorDomain
// .m文件中
NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain";
- define关键字使用
#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里面有多行代码 ,我们在写这种宏的时候可以用换行符来分割。我们自己的项目中非常多的地方需要用到登陆的判断,如果用户没有登录就前去登录。代码也可以写成宏。
// 前往登录
#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即可。
-
strcmp关键字
都要忘了这个C关键字,
用来比较两个字符串是否相等。
若str1==str2,则返回零;
若str1<str2,则返回负数;
若str1>str2,则返回正数。 -
dispatch_queue_get_label
用来获取当前队列的名称 -
dispatch_queue_async_safe(<#queue#>, <#block#>)
在这里主要为了做安全判断
总结起来这代码意思就是获取的线程名称如果和主线程名称相同,则当前线程是主线程,直接调用block,如果不是,调用dispatch_async(dispatch_get_main_queue(), block)。
知识点4 --- NS_OPTIONS和NS_ENUM
- NS_OPTIONS和NS_ENUM都是都C语言中enum的封装,区别NS_OPTIONS用于位移类型的enum,NS_ENUM用于普通enum。
举个例子 - NS_OPTIONS
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;位运算保证了这个数字的唯一性。以次来获取枚举组合。
- 普通枚举NS_ENUM
再拿来一个系统枚举
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文件各个方法其实可以看成宏的替换内容。
网友评论