小蓝书第一章总结
熟悉Objective-c
了解objective-c的起源
oc语言是c语言的超集,在语法上和c++十分相似,都是面向对象的语言;
oc使用的是动态绑定的消息结构而非c语言的函数调用;
oc语言只有在运行时才会检查对象类型,编译时没用这个过程,这个特性就是动态绑定,oc代码的执行由运行环境而非编译器决定 ;
oc是c的超集,意味着c的代码在oc中同样适用,其中oc的内存管理"引用计数"也类似于c中的内存管理模型,熟练c的内存管理也有助于学习oc的内存管瘤;
oc对象的内存只能分配在堆空间中,不能在栈空间中,这也意味着oc对象的内存必须直接管理;但像CGRect类的结构体分配在栈中,无需管理 ;
指针对象也在栈中;
在类的头文件中尽量少引入其他头文件
首先能不引入头文件就不引入头文件,如果有一个类的对象作为另一个类的属性,如果没有必要理解全部细节,可以采用"向前声明"的方法,及时必须使用这个对象也应将头文件的引入尽量延后,也就是在实现部分引入头文件 ;
要避免两个头文件你中有我,我中有你,而形成循环引用的问题,虽然不会死循环,但其中一个编译会出问题 ;
当无法向前声明时,比如声明遵守某个协议,可以将声明移至class-continuation中 ;
总的来说,要尽量避免各类中过于复杂的依赖关系,降低耦合度;
多用字面量语法,少用与之等价的方法
使用字面量语法可以缩减源代码长度,使其更为易读。
字面数值
传统方法:
objectivec
NSNumber *someNumber = [NSNumber numberWithInt:1];
字面量方法:
objectivec
NSNumber *someNumber = @1;
objectivec
NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
int x = 5;
float y = 6.32f;
NSNumber *expressionNumber = @(x * y);
字面量数组
传统方法:
objectivec
NSArray *animals = [NSArray arrayWithObjects:@"cat", @"dog", @"mouse", @"badger", nil];
NSString *dog = [animals objectAtIndex:1];
字面量方法:
objectivec
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];
字面量字典
传统方法:
objectivec
NSDictionary *personData = [NSDictionary dictionaryWithObjectivesAndKeys:@"Mett", @"firstName", @"Galloway", @"lastName", [NSNumber numberWithInt:28], @"age", nil];
NSString *lastName = [personData objectForKey:@:lastName"];
字面量方法:
objectivec
NSDictionary *personData = @{@"firstName": @"Matt", @"lastName": @"Galloway", @"age": @28};
NSString *lastName = personData[@"lastName"];
注意下两个方法键和对象的顺序不同 ;
可变数组与字典
通过取下标操作,可以访问数组中的某个元素或者字典中的某个键对应的元素,如果数组和字典是可变的(mutable),那么也能通过下标修改其中的元素值
objectivec
mutableArray[1] = @"dog";
mutableDictionary[@"lastName"] = @"Galloway";
局限性
字面量语法除了字符串以外,所创建出来的对象必须属于Foundation框架才行。如果自定义了这些类的子类,则无法用字面量语法创建其对象。并且使用字面量语法创建出来的字符串、数组、字典对象都是不可变的;
还有用字面量语法时,若值中有nil,则会抛出异常;
多用类型常量,少用#define预处理命令
通常在定义常量时会用到#define, 但是使用这种方法定义的常量不会包含类型信息并且还有可能在编译之后运行时被修改从而导致问题。所以常常会用到static;
objectivec
static const NSTimeInterval kAnimationDuration = 0.3;
这种方式定义的常量包含类型信息,其好处是清楚的描述了常量的含义。由此可知,该常量类型为NSTimeInterval;
常量定义的位置也非常重要,我们最好不要将常量定义在头文件中,若你定义在头文件中,又被其他的文件引用了,那么该这个文件中的这个常量都会被其替换掉;
变量一定要同时使用static和const来声明,如果试图修改由const修饰符所声明的变量,那么编译器就会报错。static修饰符则意味着该变量仅在定义此变量的编译单元中可见。假如声明此变量时不加static,则编译器会为它创建一个"外部符号"。此时若是另一个编译单元中也声明了同名变量,那么编译器就会抛出错误信息。
有时候我们需要对外公开某个常量,比如说,用一个对象派发通知,让其他想要接收通知的对象向该对象注册,派发通知的时候需要字符串表示通知名称,我们定义一个常量,外界就可以直接使用这个常值变量来注册自己想要接收的通知即可,而不用知道实际字符串的值。此类常量需放在"全局符号表"中,以便可以在定义该常量的编译单元之外使用。
objectivec
//In the header file
extern NSString *const EOCStringConstant;
//In the implementation file
NSString *const EOCStringConstant = @"VALUE";
1.不能用预处理指令定义常量。这样定义出来的常量不含类型信息,编译器只是会在编译前据此执行查找与替换操作。即使有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量值不一致。
2.在实现文件中使用static const来定义"只在编译单元内可见的常量"。由于此常量不在全局符号表中,所以无需为其名称加前缀。
3.在头文件中使用extern来声明全局变量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以其名称应该加以区隔,通常用与之相关的类名做前缀。
用枚举表示状态,选项,状态码
枚举是一种常量命名方式。某个对象所经历的各种状态就可以定义为一个简单的枚举集。
objectivec
enum EOCConnectionState {
EOCConnectionStateDisConnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
};
编译器一般从0开始每个枚举加1来为枚举分配一个独有的编号,并且实现枚举所用的数据类型取决于编译器。
objectivec
enum EOCConnectionState = EOCConnectionStateDisConnected;
为了操作起来更加方便可以使用typedef关键字重新定义枚举类型的方式:
objectivec
typedef enum EOCConnectionState EOCConnectionState;
还可以收订指定某个枚举成员的所对应的值,那么在它之后的枚举类型的值就是在该值的基础上依次加一。
objectivec
enum EOCConnectionState {
EOCConnectionStateDisConnected = 1,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
};
使用枚举可以定义选项并且用"按位或操作符"可以组合多个选项,用"按位与操作符"可以判断是否启用某个选项;
objectivec
enum UIViewAutoresizing {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5,
};
objectivec
enum UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
if (resizing & UIViewAutoresizingFlexibleWidth) {
//UIViewAutoresizingFlexibleWidth is set
}
可以把逻辑含义相似的一组状态码放入一个枚举集中,而不需要用#define来定义。比如在创建UI元素的时候使用不同的样式,可以把这些样式声明为枚举类型。
objectivec
typedef NS_ENUM (NSUInteger, EOCConnectionState) {
EOCConnectionStateDisConnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
};
switch (_currentState) {
EOCConnectionStateDisConnected:
//Handle disconnected state
break;
EOCConnectionStateConnecting:
//Handle connecting state
break;
EOCConnectionStateConnected:
//Handle connected state
break;
}
1.应该用枚举来表示状态机的状态、传递给方法的选项以及状态码等值,给这些值起个易懂的名字。
2.如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么就将各选项值定义为2的幂,以便通过按位或操作将其组合起来。
3.用NS_ENUM与NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。这样做可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。
4.在处理枚举类型的switch语句中不要实现default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。