本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含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
, 就相当于是既有向东的加速度, 又有向南的加速度.
控制台的打印为: