<<Effective Objective -C 2.0>>阅读笔记只是作为骚栋的自己的心得,建议各位看官自己去看<<Effective Objective -C 2.0>>,五星推荐,推荐理由:面试储备必备、内存性能优化、提高代码质量.非广告~
前言
<<Effective Objective -C 2.0>>只是做阅读的笔记,所有的笔记只是把52个相关有效方法进行总结以及阐述骚栋的观点.<<Effective Objective -C 2.0>>这本书对实际开发过程中还是有着很大帮助的,尤其是项目的性能优化方面有着巨大的帮助,而且如果你想当一个iOS面试官,或者说你要去面试iOS,那么<<Effective Objective -C 2.0>>这本书我推荐给你.我将按照读书的每一个方法进行记录.看官们可以自行查看.
熟悉Objective-C(第一章标题)
全书总共分为7个章节,这一篇文章,我们主要说一下第一章节熟悉Object-C的内容.这一章节总共有五条建议,下面我们一一道来.
-
了解 Objective-C的起源
这个前面就说了一堆关于OC的家族史,我们就暂时跳过了,但是我们要说说这个OC中独有的"消息结构",其实我更喜欢称之为"消息发送机制".首先从语法外表上来说,相比于C语言的"函数调用"和其他语言例如java,js中通过调用某一个对象中的某一个方法都是使用"点语法".而OC中使用的是一个[]
来表明某一个对象调用了某一个方法.如下示例所示.那么从本质上来说,OC语言是总是在运行的时候才去查找所需要执行的方法.(可以查看<<Effective Objective -C 2.0>>补充(一):OC中的消息机制(转载))
NewObject *object = [[NewObject alloc]init];
[object action];
另外这一部分还说了一个名词,那就是指针,指针是什么?有一本书的名字诠释指针的定义,叫做"指针是C语言中一个闪亮的星星",如果C语言中什么最重要,那么无疑是三样,一是指针,二是链表,三是结构体.在OC中我们到处可见的就是*
,作为指针,OC中没有想C中那么要求严格了,但是这里有一个问题,可能连作为资深的你都不知道,那就是 我们都知道OC中的对象是存储在堆区的,那么作为指向对象地址的指针是存储在什么区?难道也是堆区?,正确答案是栈区.
OC作为C语言的超集,添加了面向对象的特性.作为一个iOS开发人员,我们使用对象那真是到了如火纯青的地步了.在OC中我们也会经常用到结构体,比如CGRect,在C语言中结构体的存在意义类似于OC中的对象。下面我们就比较一下两者的区别。
首先,结构体是存储于内存的栈区的,而OC中的对象则是存储在内存的堆区;再者,C语言中的结构体要比OC的对象中对内存性能的影响更小。主要是原因是在于OC对象的创建需要额外的开销,比如分配及释放堆内存等。
-
在类的头文件中尽量少引入其他头文件
我们在一个类中经常会使用到另外的一个类,那么我通常需要到导入头文件,通常我们导入使用#import来导入头文件,我们看下面一种情况,我们需要把NewObject
这个类的对象作为ViewController
的一个属性,而且需要暴露出来,所以把属性写在View Controller
的头文件中.我们可能就是用#import "NewObject.h"
来导入到头文件中.如下所示.
#import <UIKit/UIKit.h>
#import "NewObject.h"
@interface ViewController : UIViewController
@property(nonatomic,strong)NewObject* obj;
@end
虽然这样是可行,但是却不够优雅,在编译的时候,我们只需要知道NewObject
这个类是存在的即可,我们不需要这个类的全部细节,所以这里我们使用@class导入对象的头文件即可.如下所示.
#import <UIKit/UIKit.h>
@class NewObject;
@interface ViewController : UIViewController
@property(nonatomic,strong)NewObject* obj;
@end
那么这么做到底有什么好处呢?或者说,这么做到底有什么样的优势呢?使用@class将引入头文件的时机尽量延后,只在确有需要时才引入,这样就可以减少类的使用者所需引入的头文件数量.假设使用#import "NewObject.h"
来导入到头文件中,那么会一并引入NewObject的所有内容.若此过程持续下去,则要引入许多根本用不到的内容,会大大的增加编译时间.另外使用@class还解决了另外的一个问题,那就是循环引用问题.下面我们就举例说明一下关于@class解决循环引用的示例.
我们假设有两个类,分别叫FirstObject和SecondObject,在FirstObject中有一个SecondObject属性对象,同时在SecondObject中有一个FirstObject属性对象,同时我们都是使用#import导入头文件,所以两个类的情况就如下所示了.
#import <Foundation/Foundation.h>
#import "SecondObject.h"
@interface FirstObject : NSObject
@property(nonatomic,strong)SecondObject *sencondObject;
@end
#import <Foundation/Foundation.h>
#import "FirstObject.h"
@interface SecondObject : NSObject
@property(nonatomic,strong)FirstObject *firstObject;
@end
但是,这样就会出现循环引用问题,这是为什么呢?当解析其中的一个头文件的时候,编译器会发现它引入另一个头文件,而那个头文件又回过头来引入第一个头文件,这样反反复复,会最终导致一个类无法被正确编译.如下图所示.
那么我们改如何解决这种循环引用问题呢?我们只需要把两个头文件的任意一个导入换成@class方式即可,前面我们说过@class只是说明有这个类的存在,并不会导入这个类的全部信息.比如我们把FirstObject中的头文件如下修改,然后就不会再出现编译错误了.
#import <Foundation/Foundation.h>
@class SecondObject;
@interface FirstObject : NSObject
@property(nonatomic,strong)SecondObject *sencondObject;
@end
-
多用字面量语法,少用与之等价的方法
其中,对于字面量语法,这几年学习的iOS童鞋都会使用.这种字面量语法从Objective-C 1.0就开始出现了.时代已经相当久远了~对于从iOS 6走过来的我们来说,我们只需要知道如何使用即可,像NSString、NSArray、NSDictionary、NSNumber这种不可变类型的对象都可以使用字面量语法.示例如下所示.
NSString *string = @"字面量语法";
NSArray *array = @[@"栋哥",@"菜哥",@"狗哥"];
NSDictionary *dictionary = @{@"key":@"value"};
NSNumber *number = @1;
使用字面量语法,到底有什么优势呢?其实主要是更为简单易读.我们看下面两种创建方式.我们发现使用字面量语法创建字符串更加的简答易懂.
NSString *string = @"字面量语法";
NSString *string = [NSString stringWithString:@"字面量语法"];
而且,现在如何使用下面的那种方式创建,那么会出现警告.如下图所示.
-
多用类型常量,少用#define预处理指令
在实际开发的过程中,我们经常会使用预处理指令来定义我们常用的常量,例如我们定义一个int类型的常量,如下所示.
#define KLength 12
但是这样使用预处理指令到底会出现什么弊端呢?其实,整体上来说还是规范的问题,首先使用预处理指令没有表明常量的类型信息,所有不能很好的表明的描述常量的定义;而且,预处理过程会把碰到的所有的KLength替换成12,这样的话,假设此指令声明在某个头文件中,那么所有引入了这个头文件的代码,KLength都会被替换掉.
那么,我们该如何解决上面阐述的两个问题呢,我们可以使用以下的代码来定义KLength这个常量.我们这样定义之后,既解决了引入头文件无故替换问题,而且还包含常量的类型信息.
static const int KLength = 12;
-
用枚举表示状态、选项、状态码
使用枚举来表示状态应该是一个很常见的使用,比如我以前写过的SDLaunch:一个杂七杂八,却功能完整的广告引导页,SDLaunch的类型就有四种,我在使用过程中就是用了枚举来表示不同的类型如下所示.
typedef enum {
ADLaunchViewController,//广告类型
GreenhandLaunchViewController,//轮播图新手导引类型
GifBackgroundLaunchViewController,//gif图背景类型
RollImageLaunchViewController//滚动图片类型
} LaunchViewControllerType;
还有使用枚举来表示选项,这个是我们经常在一些头文件中看到的,用枚举来表示选项和枚举来表示状态是不一样的,一般我们用枚举来表示状态的时候是会选择其中的一个枚举值,而用枚举来表示选项则是可能是多种多样的选择组合.换句话说就是状态是单选,而选项是多选的.
那么我们如何使用枚举来表示选项呢?如下所示.
//正确的选项枚举示例
typedef enum {
firstType = 0,
secondType = 1<<0,
thirdType = 2<<0,
fourthType = 3<<0
}enumType;
这里我们看到了我们使用了移位符<<,首先说明一下什么叫移位符.
位移位运算符是将数据看成二进制数,对其进行向左或向右移动若干位的运算。位移位运算符分为左移和右移两种,均为双目运算符。第一运算对象是移位对象,第二个运算对象是所移的二进制位数。
也就是说1<<0的值为二级制的10,2<<0的值为二级制的100,那么为什么要这么做呢?我们假设不使用移位符,那么我们直接使用1,2,3,4来给枚举赋值.如下所示.
//错误的选项枚举示例
typedef enum {
firstType = 0,
secondType = 1,
thirdType = 2,
fourthType = 3
}enumType;
假设我们需要是的thirdType和secondType两个选项,我们可以通过"按位或操作符"来组合.如下所示.但是这样就真的没有错误了吗?使用"按位或操作符"其实是组合两个枚举值的值,也就说newType现在的值是3,那么跟enumType newType = fourthType;这样是一个意义了,并不能表达选择了多个枚举值.
enumType newType = secondType|thirdType;
假设我们使用了移位符<<那一套的枚举值就不会这样的问题了,我们来看一下这是为什么呢?我们还是使用** enumType newType = secondType|thirdType;这时候,newType的枚举值是多少呢?是二进制的110,如果说enumType newType = fourthType;**,那么枚举值也会发生改变为1000,两者并不是相等的,这样我们就区分开了.示例图像如下所示.
和枚举经常配合使用的经常的switch分支语句,我们知道switch分支语句在最后都会默认的实现default语句,这里,书中不建议我们把这个分支进行实现,这样的话,如果加入新枚举之后,那么编译器就会提醒开发者,switch并没有处理所有的枚举.
结束
关于<<Effective Objective -C 2.0>>中的第一章的五个注意点就写到这里,由于本文为笔记形式,所以需要更深刻的理解书中内容,还是建议读者去自行阅读<<Effective Objective -C 2.0>>这本书,下面发一个PDF的书籍传送门.大家可以去看PDF版的,如果有任何问题欢迎联系骚栋,我们一同探讨.谢谢大家.