《Effective OC 2.0》读书笔记(一)熟悉OC

879 阅读5分钟

OC起源

  1. OC代码中方法名可能极长,好处是易读。
  2. OC使用“消息结构”机制,运行时所执行的代码由运行环境决定。
  3. 接收消息的对象也是在运行时处理,该过程叫做“动态绑定”。
  4. OC采用引用“计数方式”管理内存。
  5. OC中对象的内存总是分配在堆空间。
  6. 深浅拷贝
    • 深拷贝:创建一个新的对象,将原对象的所有元素值拷贝;
    • 浅拷贝:将原对象的引用赋给新对象,只是对原对象的引用。

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

  1. 当头文件中只需要知道类名时,采用向前声明【@class ClassName】的办法来告知编译器。
  2. 如果无法使用向前声明,比如声明某类遵循一项协议,可将“该类遵循某协议”的声明放在分类中。
  3. 将引入头文件的时机尽量延后,确定需要时才引入,减少类的使用者所需引入的头文件数量,降低类之间的耦合。
  4. 引入头文件过多,会发现很多不需要的内容也被引入了,会增加编译时间。
  5. 当两个类的头文件被互相引用时,会导致“循环引用”。虽然使用#import不会导致死循环,但是会导致这两个类中的其中一个无法被正确编译。
  6. 协议可单独放在一个头文件中。

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

  1. 字面量语法

    • 优点:简明扼要
    • 缺点
      • 所创建的对象必须属于Foundation框架;
      • 使用字面量语法创建的字符串、数组、字典对象都是不可变类型,可变对象要使用mutableCopy复制一份。
  2. 字面数值

    NSNumber类,可处理多种类型数值,比如整数、浮点数、布尔值等,也适用于表达式。

    NSNumber *intNumber = @1;
    NSNumber *floatNumber = @.2f;
    NSNUmber *doubleNumber = @3.132132324423432;
    NSNumber *boolNumber = @NO;
    NSNUmber *charNumber  = @'a';
    NSNumber *expressionNumber = @(2 + 3);
    
  3. 字面量数组

    • 创建

      • 使用字面量语法
      NSArray *arr = @[@1,@2];
      
      • 使用方法
      NSArray *arr = [NSArray arrayWithObjects:@1,@2,nil];
      
      • 区别点
      !使用字面量语法创建数组时,如果有空元素会抛出异常;使用这种方式创建数组相当于先创建一个数组,再将方括号内的对象添加至数组中。
      !使用arrayWithObjects:方法创建数组时,如果有空元素,不会崩溃,但该数组的元素仅到该元素为止,因为使用方法添加数组元素时发现nil则结束。
      
    • 取值

      • 使用字面量语法
      NSNUmber *number = arr[1]; //也叫做“取下标”操作
      
      • 使用方法
      NSNUmber *number = [arr objectAtIndex:1];
      
  4. 字面量字典

    字典:一种映射型数据结构,以key:value形式存储数据。key、value都必须是OC对象。

    • 创建

      • 使用字面量语法
      NSDictionary *dic = @[@"key1":@"value1",@"key2":@"value2"];
      
      • 使用方法
      NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"value1":@"key1",@"value2":@"key2",nil]
      
      • 区别点
      ! 使用字面量语法创建字典时,元素形式是key:value;使用方法创建字典时,元素形式是value:key。
      ! 使用字面量语法创建字典时,如果值为nil,会抛出异常。
      ! 使用方法创建字典时,会在首个nil之前停下,并抛出异常。
      
    • 取值

      • 使用字面量语法
      NSString *str = dic[@"key1"];  //也叫做"取下标"操作
      
      • 使用方法
      NSString *str = [dic objectForKey:@"key1"];
      

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

  1. #define 预处理指令,遇到宏则替换宏为常值,不做其他处理。如果该值被重新定义也不会有警告信息,会导致程序中的变量值不一致。

  2. 类型常量 【 static const NSString*名称 = 赋值;】

    • 说明

      • const 修饰符所声明的变量,若被修改编译器会报错;
      • static 修饰符表示该变量仅在定义该变量的编译单元内可见。
      • 使用这种方式定义的变量,编译器会像#define指令那样将变量替换为常值,与#define不同的是这种方式定义的变量有类型信息。
    • 命名规则

      • 若常量局限于某“编译单元”,即实现文件之内,在前面加上字母k;
      • 若常量在类之外可见,通常以类名作为前缀。
  3. 外界可见的常值变量,这类变量需放在“全局符号表”中。

    • 说明:extern关键字,告诉编译器该变量在全局符号表中,编译器无需查看它的定义,允许代码中使用此常量。
    • 命名:为避免冲突,最好用相关的类名作为变量名的前缀。
    • 定义规则:
    extern  NSString *变量名;  //.h文件中
    NSString *变量名 = 赋值;   //.m文件中
    

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

  1. 枚举是一种常量命名方式。编译器会为枚举分配一个独有的编号,默认从0开始,每个枚举递增1,也可自定义枚举值。

  2. switch语句中的应用

    • 在switch语句中,用枚举来表示状态机的状态时,不要实现default分支。
    • 添加了新的枚举时,如果switch中实现了default会提示switch语句中未处理所有枚举。
  3. Foundation框架中定义了 NS_ENUM、NS_OPTIONS宏。

    • 这两个宏是用#define定义的,具备向后兼容能力,如果目标平台编译器支持新标准,就使用新语法,否则使用旧语法。
    • 可以用来定义枚举类型,需指明底层数据类型,确保枚举开发是用开发者所选择的底层数据类型实现的。
  4. NS_ENUM,同语言中的enum,类型一般定为NSInteger。

     typedef  NS_ENUM (类型 , 名称) {
         …
     };
    
  5. NS_OPTIONS

    • 枚举选项可以通过按位或运算实现多个选项组合成一个新的选项;
    • 选项的值一般为2的幂;
    • 类型一般定义为NSInteger
      typedef  NS_OPTIONS (类型, 名称) {
       …
      };