2017-06-28 本文已影响0人
小冰山口
本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.
这是一本很经典的OC的书, 不过一直没时间看, 最近想好好撸一遍, 于是就想写一点读书心得, 按照章节来吧, 这本书分为7个章节:
Chapter 1. 熟悉Objective-C
Chapter 2. 对象, 消息, 运行时
Chapter 3. 接口与API设计
Chapter 4. 协议与分类
Chapter 5. 内存管理
Chapter 6. Block与GCD
Chapter 7. 系统框架
今天先介绍下第一章的内容: 熟悉OC
这一章内容不多, 也很简单, 大家都知道的事情我就不多说了, 就说两个需要注意的点:
- 类型常量与
#define预处理指令
- 位移枚举的使用
(一)类型常量与#define预处理指令
当你在写代码的时候, 有时候直接写上魔法数字是一种很low的做法, 这个时候, 有两种解决方案:
-
#define预处理指令
- 类型常量
比如你要定义一个动画的持续时间为0.5秒, 你可以:
#define kAnimationDuration 0.5
也可以:
static const NSTimeInterval kAnimationDuration = 0.5;
但这两种方案其实效果差不多, 但是第二种方案包含了常量的类型, 你明确地知道它是NSTimeInterval类型的, 虽然NSTimeInterval就是double类型,但这个类型转换明显增强了代码可读性, 让阅读代码的人明确知道, 这个变量就是与动画持续时间相关的.
如果你不打算公开这个常量供外界使用的话, 就写在.m文件里
static和const两个关键字要同时使用.
原因如下:
♦︎ 如果试图修改const修饰符所声明的变量, 那么编译器就会报错.
static const NSTimeInterval kAnimationDuration = 0.5;
像这种类型的变量我们只需要使用即可, 不希望被改动, 那么就需要用const关键字来修饰
♦︎ 而static关键字则意味着该变量仅仅在此变量的编译单元中可见.
编译器每收到一个编译单元(.m文件<你可以这么理解>), 就会输出一份"目标文件"(object file).
♦︎ 假如你声明变量的时候不加static, 则编译器会为它创建一个"外部符号"(external symbol). 此时若是另一个.m文件也声明了同名变量, 那么编译器就会抛出错误消息.
♦︎ 实际上, 如果一个变量既声明为static, 又声明为const, 那么编译器根本不会创建符号, 而是直接像#define预处理指令一样, 把所有遇到的变量都替换为常量, 不同的是:(再强调一遍) 这种方法是带有类型信息的
但如果你的类型常量就是要公开给全局使用, 那么就需要用extern关键字来修饰, 最常见的比如一些通知的名称:
UIApplicationDidEnterBackgroundNotification
UIApplicationWillEnterForegroundNotification
比如在.h中你这样声明:
extern NSString *const UIApplicationDidEnterBackgroundNotification;
在.m文件中定义:
NSString *const UIApplicationDidEnterBackgroundNotification = @"UIApplicationDidEnterBackgroundNotification";
这样全局的命名必须在前面加上类名.
像之前那个例子, 只是在自己的编译单元使用的, 只需要加上小写字母k即可:
static const NSTimeInterval kAnimationDuration = 0.5;
(二)位移枚举
系统已经为我们提供了一些快速创建枚举变量的宏, 比如NS_ENUM, NS_OPTIONS, 前者相信大家都很熟悉了, 我来说说后者:
无论是在系统框架中, 还是在SDWebImage这样的第三方框架里, 位移枚举都是广泛使用的, 位移枚举可以使你同时使用多个枚举值:
比如在SDWebImage中, 你就常常见到这样的代码:
SDWebImageDownloaderOptions downloaderOptions = 0;
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
我举一个简单的例子来说明一下:
假设我现在创建了一个关于方向的位移枚举:
typedef NS_OPTIONS(NSUInteger, YFDirection){
YFDirectionNone = 0, // 没有方向
YFDirectionEastward = 1 << 0, // 向东
YFDirectionWestward = 1 << 1, // 向西
YFDirectionSouthward = 1 << 2, // 向南
YFDirectionNorthward = 1 << 3, // 向北
};
比如我想判断一个物体的加速度方向, 一个物体可能在多个方向上都有加速度, 那么这个就是普通枚举无法完成的, 这个时候就需要用到上面的位移枚举
- (void)judgeDiretionWithOptions:(YFDirection)directionOption;
当我点击屏幕的时候, 就调用上面这个方法:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self judgeDiretionWithOptions: kNilOptions];
}
- judgeDiretionWithOptions:方法的实现:
- (void)judgeDiretionWithOptions:(YFDirection)directionOption {
if (directionOption & YFDirectionEastward) {
NSLog(@"向东");
}
if (directionOption & YFDirectionWestward) {
NSLog(@"向西");
}
if (directionOption & YFDirectionSouthward) {
NSLog(@"向南");
}
if (directionOption & YFDirectionNorthward) {
NSLog(@"向北");
}
NSLog(@"%zd", directionOption);
}
如果我传的参数为YFDirectionEastward | YFDirectionSouthward, 就相当于是既有向东的加速度, 又有向南的加速度.
控制台的打印为:
控制台的打印结果
至于位移的原理, 这里就不再赘述了. 希望大家能够将位移枚举灵活地运用到项目当中.
PS. 本人有若干成套学习视频, 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.
本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.
这是一本很经典的OC的书, 不过一直没时间看, 最近想好好撸一遍, 于是就想写一点读书心得, 按照章节来吧, 这本书分为7个章节:
Chapter 1. 熟悉Objective-C
Chapter 2. 对象, 消息, 运行时
Chapter 3. 接口与API设计
Chapter 4. 协议与分类
Chapter 5. 内存管理
Chapter 6. Block与GCD
Chapter 7. 系统框架
今天先介绍下第一章的内容: 熟悉OC
这一章内容不多, 也很简单, 大家都知道的事情我就不多说了, 就说两个需要注意的点:
- 类型常量与
#define预处理指令 - 位移枚举的使用
(一)类型常量与#define预处理指令
当你在写代码的时候, 有时候直接写上魔法数字是一种很low的做法, 这个时候, 有两种解决方案:
-
#define预处理指令 - 类型常量
比如你要定义一个动画的持续时间为0.5秒, 你可以:
#define kAnimationDuration 0.5
也可以:
static const NSTimeInterval kAnimationDuration = 0.5;
但这两种方案其实效果差不多, 但是第二种方案包含了常量的类型, 你明确地知道它是NSTimeInterval类型的, 虽然NSTimeInterval就是double类型,但这个类型转换明显增强了代码可读性, 让阅读代码的人明确知道, 这个变量就是与动画持续时间相关的.
如果你不打算公开这个常量供外界使用的话, 就写在.m文件里
static和const两个关键字要同时使用.
原因如下:
♦︎ 如果试图修改const修饰符所声明的变量, 那么编译器就会报错.
static const NSTimeInterval kAnimationDuration = 0.5;
像这种类型的变量我们只需要使用即可, 不希望被改动, 那么就需要用const关键字来修饰
♦︎ 而static关键字则意味着该变量仅仅在此变量的编译单元中可见.
编译器每收到一个编译单元(.m文件<你可以这么理解>), 就会输出一份"目标文件"(object file).
♦︎ 假如你声明变量的时候不加static, 则编译器会为它创建一个"外部符号"(external symbol). 此时若是另一个.m文件也声明了同名变量, 那么编译器就会抛出错误消息.
♦︎ 实际上, 如果一个变量既声明为static, 又声明为const, 那么编译器根本不会创建符号, 而是直接像#define预处理指令一样, 把所有遇到的变量都替换为常量, 不同的是:(再强调一遍) 这种方法是带有类型信息的
但如果你的类型常量就是要公开给全局使用, 那么就需要用extern关键字来修饰, 最常见的比如一些通知的名称:
UIApplicationDidEnterBackgroundNotification
UIApplicationWillEnterForegroundNotification
比如在.h中你这样声明:
extern NSString *const UIApplicationDidEnterBackgroundNotification;
在.m文件中定义:
NSString *const UIApplicationDidEnterBackgroundNotification = @"UIApplicationDidEnterBackgroundNotification";
这样全局的命名必须在前面加上类名.
像之前那个例子, 只是在自己的编译单元使用的, 只需要加上小写字母k即可:
static const NSTimeInterval kAnimationDuration = 0.5;
(二)位移枚举
系统已经为我们提供了一些快速创建枚举变量的宏, 比如NS_ENUM, NS_OPTIONS, 前者相信大家都很熟悉了, 我来说说后者:
无论是在系统框架中, 还是在SDWebImage这样的第三方框架里, 位移枚举都是广泛使用的, 位移枚举可以使你同时使用多个枚举值:
比如在SDWebImage中, 你就常常见到这样的代码:
SDWebImageDownloaderOptions downloaderOptions = 0;
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
我举一个简单的例子来说明一下:
假设我现在创建了一个关于方向的位移枚举:
typedef NS_OPTIONS(NSUInteger, YFDirection){
YFDirectionNone = 0, // 没有方向
YFDirectionEastward = 1 << 0, // 向东
YFDirectionWestward = 1 << 1, // 向西
YFDirectionSouthward = 1 << 2, // 向南
YFDirectionNorthward = 1 << 3, // 向北
};
比如我想判断一个物体的加速度方向, 一个物体可能在多个方向上都有加速度, 那么这个就是普通枚举无法完成的, 这个时候就需要用到上面的位移枚举
- (void)judgeDiretionWithOptions:(YFDirection)directionOption;
当我点击屏幕的时候, 就调用上面这个方法:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self judgeDiretionWithOptions: kNilOptions];
}
- judgeDiretionWithOptions:方法的实现:
- (void)judgeDiretionWithOptions:(YFDirection)directionOption {
if (directionOption & YFDirectionEastward) {
NSLog(@"向东");
}
if (directionOption & YFDirectionWestward) {
NSLog(@"向西");
}
if (directionOption & YFDirectionSouthward) {
NSLog(@"向南");
}
if (directionOption & YFDirectionNorthward) {
NSLog(@"向北");
}
NSLog(@"%zd", directionOption);
}
如果我传的参数为YFDirectionEastward | YFDirectionSouthward, 就相当于是既有向东的加速度, 又有向南的加速度.
控制台的打印为:
控制台的打印结果