美文网首页
Effective Objective-C 2.0(读书笔记)一

Effective Objective-C 2.0(读书笔记)一

作者: 木子影 | 来源:发表于2017-03-15 13:56 被阅读12次

编写高质量iOS与OS X代码的52个有效方法

第一章 熟悉OC

一:Objective-C 的起源

1、对象所占内存总是分配在“堆空间”(heap)中,而绝对不会分配在"栈"(stack)上。
2、分配在堆中的内存必须直接管理,而分配在栈上用于保存变量的内存则会在其栈帧弹出时自动清理。
3、OC将堆内存管理抽象出来,不需要用malloc、free来分配或者释放内存所占内存,这个内存管理框架叫"引用计数"。
4、不含*的变量可能会使用栈空间。因为使用对象的话会造成额外开销,例如内存的分配以及释放。

二:再类的头文件中尽量少引入其他头文件

1、除非有必要,否则不要引入头文件。一般来说,应该在某个类的头文件中使用向前声明来提及别的类,并在实现文件中引入那些类的头文件。这样做可以尽量降低类之间的耦合(coupling),同时减少编译时间。

例如AClass要包含BClass,并将BClass作为自身的一个属性,原本写法:
#import <Foundation/Foundation.h>
#import "BClass.h"

@interface AClass : NSObject
@property (nonatomic,strong)BClass *b;
@end

现在可以改成:
#import <Foundation/Foundation.h>
@class BClass  //向前声明

@interface AClass : NSObject
@property (nonatomic,strong)BClass *b;
@end

#import "AClass.h"
#import "BClass.h"

@implementation AClass
//implementation of methods
@end

2、有时无法使用向前声明,比如要声明某个类遵循一项协议。这种情况下,尽量把"该类遵循某项协议"的这条声明移至"class-continuation分类"中。如果不行的话,就该把协议单独放在一个头文件中,然后将其引入。

三:多用字面量语法,少用与之等价的方法

字面量语法实例如下

NSString *str = @"LRG read hard";

如果不用这种语法就需要用常见的alloc以及init来分配并初始化对象了。使用字面量语法可以缩减源码的长度,另其更为易读。
1、应该少用字面量语法来创建字符串、数值、数组、字典。与创建此类对象的常规方法相比,这么做更加简明扼要。
2、应该通过取下表操作来访问数组下标或字典中键所对应的元素。
3、用字面量语法创建数组或字典时,若值中有nil,则会抛出异常。因此务必确认值里不含nil.

四:多用类型常量,少用#define预处理指令

   OC中"编译单元"一词通常指每个类的实现文件(以.m为后缀名)。编译器每收到一个编译单元,就会输出一份“目标文件”(object file)。
   如果一个变量既声明为static,又声明为const,那么编译器根本不会创建符号,而是会像#define预处理指令一样,把所有遇到的变量都替换为常值。不过这种方式定义的常量带有类型信息。如果不加static,编译器会为它创建一个“外部符号(external symbol)”。此时若是另外一个编译单元也声明了同名变量,那么编译器就会抛出一条错误消息。

需要放在"全局符号表"(global symbol table)中以便可以在定义该常量的编译单元外使用。定义方式如下:

// In the header file
extern NSString *const LRGConst;

//In the implementation file 
NSString *const LRGConst = @"value"
extern关键字告诉编译器,在全局符号表中将会有一个叫LRGConst的符号。编译器无须查看其定义,即允许代码使用此常量。它知道当连接成二进制文件后肯定能找到这个常量。

1、不要用预处理指令定义常量。这样定义出来的常量不含类型信息,编译器只是会再编译钱据此执行查找与替换操作。即使有人重新定义了常量值,编译器也不会产生警告信息,这将导致应用程序中的常量数值不一致。
2、再实现文件中使用static const来定义“只在编译单元内可见的常量”(translation-unit-specific constant).由于此类常量不在全局符号表中,所以无需为其名称加前缀。
3、在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值。这种常量要出现在全局符号表中,所以名称应加以区隔,通常用与之相关的类名做前缀。

五:用枚举表示状态、选项、状态码

 enum LRGLifeState{
    LRGLifeStateOne,
    LRGLifeStateTwo,
    LRGLifeStateThree,
};
typedef enum LRGLifeState LRGLifeSate; //类型重定义
LRGLifeState state = LRGLifeStateOne; //调用方式简洁一些



C++11修改了枚举的特性。其中一项改动:可以指明用何种“地城数据类型”(underlying type)来保存枚举类型的变量。这样做的好处是,可以镶嵌声明枚举变量了。若不指定底层数据类型,则无法向前声明枚举类型,因为编译器不清楚底层数据类型的大小,所以在用到该枚举类型时,不知道给该变量分配多少内存空间。

指定数据底层的语法是: 
enum LRGLifeState : NSInteger {/*...*/}`
也可以在向前声明中指定底层数据类型`
enum LRGLifeState : NSInteger`

还可以不适用编译器所分配的序号,而是手工指定某个枚举成员所对应的值。如法如下:

enum  LRGLifeState {
       LRGLifeStateOne = 1,
       LRGLifeStateTwo,
       LRGLifeStateThree,
};
  接下来的几个枚举都会在上一个的基础上递增1.



对于可以彼此组合的选项,只要枚举定义的对,各选项之间就可以通过"按位操作符"(bitwise OR operator)来组合。

enum LRGLifeState{
       LRGLifeStateOne                =0,
       LRGLifeStateTwo                =1<<0,
       LRGLifeStateThree             =1<<1,
       LRGLifeStateFour               =1<<2,
       LRGLifeStateFive                =1<<3,
       LRGLifeStateSix                  =1<<4,
       LRGLifeStateSeven            =1<<5,
}



Foundation框架定义了一些辅助的宏,用这些宏来定义枚举类型,也可以指定用于保存枚举值的底层数据类型。这些宏具备向后兼容(backward compatibility)能力。NS_ENUM用于定义普通的枚举类型,NS_OPTIONS用于定义包含一系列选项的枚举类型。用法如下

typedef NS_ENUM(NSUInteger,LRGLifeState){
         LRGLifeStateOne,
         LRGLifeStateTwo,
         LRGLifeStateThree,
         LRGLifeStateFour,
}

typedef NS_ENUM(NSUInteger,LRGLifeState){
        LRGLifeStateOne      = 1<<0,
        LRGLifeStateTwo      = 1<<1,
        LRGLifeStateThree   = 1<<2,
        LRGLifeSateFour      = 1<<3,
}



再用Switch中最好不要有default分支。这样对于新加的分支会在default中处理,而且编译器不会报错。不加的话,就要处理所有的分支。
1、应该用枚举来表示状态机的状态、传递方法的选项以及状态码等值,给这些值起个易懂的名字。
2、如果把传递给某个方法的选项表示为枚举类型,而多个选项又可同时使用,那么就讲个选项值定义为2的幂,以便通过按位或操作将其组合起来。
3、用NS_ENUM与NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型。遮掩工作可以确保枚举是用开发者所选的底层数据类型来实现的,而不会采用变化一起所选的类型。
4、在处理枚举类型的switch语句中不要实现default分支。这样的话,加入新枚举之后,编译器就会提示开发者:switch语句并未处理所有枚举。

相关文章

网友评论

      本文标题:Effective Objective-C 2.0(读书笔记)一

      本文链接:https://www.haomeiwen.com/subject/bvbhnttx.html