Effective Objective-C 2.0总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ldszw/article/details/50757270

一、了解Objective-C语言

1> Objective-C的起源是Smalltalk,使用的是”消息结构”而非”函数调用”

两者的区别:
1.前者运行时所执行的代码由运行环境来决定,无论是否多态,总是在运行时才会去查找所要执行的方法(动态绑定)。 而后者也是由编译器决定,如果在运行时就会按照”虚方法表”来查出到底应该执行那个函数实现

2> Objective-C的重要工作都由“运行期组件”而非编译器来完成。使用OC的面向对象特性所需的全部数据结构及函数都在运行期组件里面

eg:
运行期组件中含有全部内存管理方法。运行期组件本质上就是一种与开发者所编代码相链接的”动态库“,其代码能把开发者编写的所有程序粘合起来。

好处:
只需要跟新运行期组件,便可提升应用程序的性能

3> 对象所占内存总是分配在“堆空间”

NSString *someString = @"The string";
NSString *anotherString = someString;

此内存图演示了一个分配在堆中的NSString实例,有两个分配在栈上的指针变量指向该实例

分配在堆中内存必须直接管理,而分配在栈上用于保存变量的内存则会在其栈帧弹出时自动清理。Objective-C将内存管理抽象出来,不需要用malloc及free来分配或释放对象所占内存

二、在类的头文件中尽量少引入其他头文件

1> 在.h文件中引入一个类的时候直接@class XXX(向前声明)就行,只需要告诉编译器有一个类名叫XXX。而在.m文件中则需要#import ”XXX.h“,因为使用后者必须知道其所有接口细节。而且向前声明也解决了两个类相互引用的问题

2> 有的时候必须要在头文件中引入其他头文件。比如某个类继承自某个超类,则必须引入定义那个超类的头文件。同理,如果写的类遵从某个协议,那么该协议必须完整定义,且不能使用向前声明。向前声明只能告诉编译器有某个协议,而此时编译器却要知道该协议中定义的方法。

// eg:
#import "EOCShape.h"
#import "EOCDrawable.h"

@interface EOCRectangle : EOCShape <EOCDrawable>
@property (nonatomic, assign) float width;
@property (nonatomic, assign) float height;
@end

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

3> 循环引用解决方案

非ARC:一端用retain,一端用assign
ARC:一端用strong,一端用weak

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

1>在用字面量语法创建数组或字典时,要确保值里不包含nil,否则会抛出异常

2>字面量语法有个小小的限制,就是除了字符串以外,所创建出来的对象必须属于Foudation框架才行。

3>使用字面量语法创建出来的字符串、数组、字典对象都是不可变的。若想要可变版本的对象,则需要复制一份。

4>应该通过去下标操作访问数组下标或字典中的键所对应的元素

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

1>常用的命名法是:若常量局限于某编译单元(.m文件)之内,则在前面加字母k;若常量在类之外可见,则通常以类名为前缀

static const NSTimeInterval kAnimationDuration = 0.3;

2> 变量一定要同时用static和const声明。如果试图修改由const修饰符所声明的变量,那么编译器就会报错。而static修饰符则 意味着该变量仅在定义此变量的编译单元中可见。编译器每收到一个编译单元,就会输出一份“目标文件”(.o文件)

假如声明此变量时不加static,则编译器会为它创建一个“外部符号”。此时若是另一个编译单元中也声明了同名变量,那么编译器就抛出错误信息。

实际上,如果一个变量既声明为static,又声明为const,那么编译器根本不会创建符号,而是会像#define预处理指令一样,把所有遇到的变量都替换为常值。不过,这种方式定义的常量带有类型信息。

3>另外一种定义方法:

// .h文件
extern NSString *const EOCStringConstant;

// .m文件
NSString *const EOCStringConstant = @"value";

extern关键字:这个关键字要告诉编译器,在全局符号表中将会有一个名叫EOCStringConstant的符号。编译器无需查看定义,既允许代码使用此常量。因为当链接成二进制文件后,编译器肯定能找到这个常量。

此类常量必须要定义,而且只能定义一次。通常将其定义在与声明该常量的头文件相关的实现文件里。由实现文件生成目标文件,编译器会在“数据断”为字符串分配存储空间。链接器会把此目标文件与其他目标文件相链接,以生成最终的二进制文件。凡是用到EOCStringConstant这个全局符号的地方,链接器都能将其解析。

注意常量的名字。为避免名称冲突,最好是用与之相关的类名做前缀。

定义常量要优于使用#define预处理指令,因为编译器会确保常量值不变。而采用预处理指令所定义的常量可能会无意中遭人修改,从而导致应用程序各个部分所使用的值互不相同。

4> const的使用其他注意点:

  • 当const修饰一个变量时,该变量会变成常量(不可修改,只读)
  • 当const修饰指针变量时,有两种修饰情况:

1.const放在*前面
// const的修饰的*p1和*p2,*p1和*p2是常量,不能通过*p1、*p2间接修改其他变量的值
const int *p1 = &age;
int const *p2 = &age;

2.const放在*后面
// const修饰的p3,p3是个常量,p3不能再指向其他变量
i nt * const p3 = &age;

5> 用const类型常量和#define xxx的区别
#define age 10,在编译的时候,编译器会把程序里面所有的age替换成10这个字面量,所以一个字面量就代表着一份内存空间。
而const int age = 10;在程序中age始终只要一份age这个常量。所以两者在性能上,const常量要好一些

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

1> C++11标准修订了枚举的某些特性。其中一项改动为:可以指明用何种“底层数据类型”来保存枚举类型的变量。这样做的好处是,可以向前声明枚举变量了。若不指定底层数据类型,则无法向前声明枚举类型,因为编译器不清楚底层数据类型的大小,所以在用到此枚举类型时,也就不知道究竟该给变量分配多少空间。

2>用枚举定义:

typedef NS_ENUM(NSInteger, BSTopicType) {
    BSTopicTypeAll = 1,
    BSTopicTypeVideo = 41,
    BSTopicTypeVoice = 31,
    BSTopicTypePicture = 10,
    BSTopicTypeWord = 29
};

猜你喜欢

转载自blog.csdn.net/ldszw/article/details/50757270