第一章:熟悉OC
第一条:OC是消息型语言,使用“消息结构”而非“函数调用”。
消息调用与函数的区别:
1.消息型语言运行时所执行的代码由编译时的环境决定而非编译器,这也就是说运行时才会检查对象类型,也就是我们常说的:runtime 运行时机制。
2.从code中看OC的语法常用方括号:[ ] ,而其他语言多用->。
第二条:在类的头文件中尽少引用其他头文件。
创建一个类你需要:
1.OC使用:头文件(header file)和实现文件(implementation file)来区隔代码。也就是我们常说的.h文件和.m文件,.h文件用于类的声明,而.m文件用于类的实现。
2.@class和#import的区别:
#import可以把目标类的全部细节拿出来传给所需文件。
@class只搞给目标有一个这样的文件,而不知道其细节。(这叫做“向前声明”该类)
我们一般在.h文件中写@class在.m中使用#import,这样就有效的降低耦合度,避免循环使用。
无法使用“向前声明”时,比如我们想要某个类遵循一项协议时,我们需要把协议声明放到“class continuation分类”中,如果这样还不行的话,那我们需要把协议放到一个头文件中,然后将其引用。关于“class continuation分类”可以读这篇文章:协议与分类
第三条:多用字面量语法,少用与之等价的语法。
OC以语法繁杂而著称,为了真强语法的易读性,我们在编程时间应该尽可能的使用“字面量语法”来避免“alloc”和“init”来为我们分配并初始化内存。什么是“字面量语法呢”,下面我来举一些栗子给大家:
1.字符串字面量
NSString * str = [[NSString alloc]initWithString:@"string"];
NSString * strZML = @"string";
2.字面量数值
NSNumber * numInt = [[NSNumber alloc]initWithInt:3];
NSNumber * numChar = [[NSNumber alloc]initWithChar:'a'];
NSNumber * numBool = [[NSNumber alloc]initWithBool:YES];
NSNumber * ZMLnumInt = @1;
NSNumber * ZMLnumChar = @'a';
NSNumber * ZMLnumBool = @YES;
3.字面量数组
NSArray * arr = [[NSArray alloc]initWithObjects:[NSString stringWithFormat:@"abc"],[[NSNumber alloc]initWithInt:1], nil];
NSString * str = [arr objectAtIndex:0];
NSArray * ZMLarr =@[@"abc",@1];
NSString * ZMLstr = arr[0];
用initWithObjects处理时,当元素有nil,方法会提前结束。而如果用字面量创建的数组,元素中有nil,则会抛出异常。提高代码的安全性。
在这个时候,作者提到了“语法糖”这一术语,大概意思是指,在计算机中与另一套语法等效开发者用起来更方便的语法。有兴趣的朋友可以看看这篇文章:[IOS黑魔法] - 语法糖
4.字面量字典
NSDictionary * dic = [[NSDictionary alloc]initWithObjectsAndKeys:@"hehe",@"name",[[NSNumber alloc] initWithInt:1],@"age", nil];
NSDictionary *ZMLdic = @{@"hehe":@"name",@1:@"age"};
很奇怪,用initWithObjectsAndKeys来创建的字典对象录如数据的顺序是:<对象>,<键>,<对象>,<键>这样的。而用字面量这用@{@“”:@“”},把键映射到对象的格式让人看上去很舒服。
5.可变对象
由于字面量创建出来的对象是默认不可变的,如果想要创建可变对象的话也很简单,调用mutableCopy方法即可,如以下code:
NSMutableString * str = [@"aaaaa" mutableCopy];
NSMutableArray * arr = [@[@"aaa",@"bbb",@"ccc"] mutableCopy];
NSMutableDictionary *dic = [@{@"hehe":@"name",@1:@"age"} mutableCopy];
6.局限性
除了字符串以外,对象必须是Foundation框架下的才可以。如果是自定义这些类的自类则无法使用字面量!针对这一点作者也给出了解决办法,就是自定义Foundation的子类,但是这样做成本太高,而且比较麻烦,所以并没有建议读者去这么做。
第四条:多用类型常量,少用#define
预处理指令:#define 我们可以用它来定义常量,但是有一下两个问题:
1.用#define创建出来的常量不含有类型信息,编译器只会这编译前据此执行查找与替换这样简单的工作。
2.用#define创建出来的常量值可以被重新定义,编译器并不会发出相应的警告信息。
3.常量命名:若常量局限于某个编译单元也就是.m文件中,则在前面加k。若常量在类之外可 见,则通常以类名为前缀。
综上所诉,作者反对使用#deine来创建常量值,而是建议读者在.m文件中使用 static const 来定义常量,这样做的好处是:
1.有static的存在,保证了只在编译单元内可见,并不在全局编译表中。
2.static 与 const 混用编译器会像#define那样将所有出现的变量替换为常值,而且带有类型信息!
若要在.h文件中定义全局常量,应使用extern来定义。这样当编译器看到.h文件中有extern开头的常量就会明白这个全局变量要放到全局符号表中。注意:为了避免用命重复,使用相关类名做为前缀。
//定义.h文件中的全局变量
extern NSString * const VCstring; //指针类型
extern const NSTimeInterval VClongTime; //其他类型
//.m中给其赋值
NSString * const VCstring = @"ID";
const NSTimeInterval VClongTime = 30;
想要详细了解宏定义的童鞋,推荐看喵神写的这篇文章:宏定义的黑魔法 - 宏菜鸟起飞手册
第五条:用枚举表示状态,选项,状态码
1.用枚举表示状态
当程序中需要定义对象的不同状态,我们通常会用一些常量来标记一些东西。这时如果你用一个枚举来表示出来这些状态则会优雅许多。下面我举一个友盟框架中的一个枚举栗子:
/**QQ消息类型*/
typedef enum {
UMSocialQQMessageTypeDefault, //非纯图片QQ消息
UMSocialQQMessageTypeImage //纯图片QQ消息
}UMSocialQQMessageType;
可以看到:友盟分享在区分消息类型是纯图片还是非纯图片状态时,就用了枚举值来处理:
a.用UMSocialQQMessageTypeDefault这一常量来标记了非纯图片QQ消息。
b.用UMSocialQQMessageTypeImage这一常量来标记了纯图片QQ消息。
c.用typedef给枚举值取了一个别名:UMSocialQQMessageType来方便调用:
/**分享到QQ好友的消息类型*/
@property (nonatomic, assign) UMSocialQQMessageType qqMessageType;
NS_ENUM: NS_ENUM 的第一个参数是用于存储的新类型的类型。在64位环境下,UITableViewCellStyle 和 NSInteger 一样有8bytes长。你要保证你给出的所有值能被该类型容纳,否则就会产生错误。第二个参数是新类型的名字。大括号里面和以前一样,是你要定义的各种值。
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
2.用枚举表示选项
当我在程序中定义选项时,尤其在定义可以组合的选项时,我们更应该学会枚举去定义你的选项列表,这样可以用“按位或操作符”来去组合你的选项。这些东西常常在许多第三方框架中出现,如果还不了解的同学可以看看这篇文章:枚举值定义选项通过按位或来实现多选,写的挺不错的。好了,接下来让我们看看具体的实现过程,就拿