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

Effective Objective-C 2.0 读书笔记 (

作者: 左左4143 | 来源:发表于2019-03-31 23:22 被阅读0次

第一章 熟悉Objective-C

第1条 Objective-C 起源

Objective-C 是C语言添加了面向对象的特性,是其超集,OC使用动态绑定的消息结构,也就是说对象在接收到一条消息之后,究竟应该执行什么代码是由运行环境而非编译环境来决定的

第2条 在类的头文件中尽量少引用其他头文件

尽量不要在某个类的.h文件中#import另一个类的.h,而应该使用向前声明@class 关键字 并且在.m文件中#import需要引入的类,因为#import会将这个类的全部内容引入 这样做可以提高类之间的耦合,增加编译时间,并且有可能循环引入

当一个类继承自另一个类或者遵从某个协议时,则必须要引入这个类的头文件

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

  • 使用字面量语法来创建字符串、数值、数组、字典
    例如使用
NSNumber *number = @3;
NSArray *array = @[@"a",@"b",@"c"];
NSDictionary *dictionary = @{@"key":@"value"};

来替换相应的类的传统的初始化方法 [NSNumber numberWithInt:3]

  • 通过取出下标操作来访问数组或字典中的元素
NSString *stringA = array[0];
NSString *stringB = dictionary[@"key"];

来替换诸如

[array objectAtIndex:0];
[dictionary valueForKey:@"key"];

等方法

这样做的好处是 可以是代码更加简明扼要,更加容易读懂,并且存在nil值时会立刻抛出异常,减少隐患

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

用预处理指令定义的常量不含类型信息,编译器只是执行简单的查找替换操作,即使常量值改变了也不会警告

替换方法

  • 在实现文件中使用static const 来定义只在便一单元内可见的常量
  • 在头文件中使用extern来声明全局常量,并在实现文件中定义它的值(这种常量是全局的要注意命名冲突)

第5条 用枚举表示状态、选项、状态码

将某个对象的每种状态都用一个便于理解的值来表示,写出来的代码更易懂

例如用枚举表示网络连接的状态码:

typedef NS_ENUM(NSUInteger, EOCConnectionState) {
  EOCConnectionStateDisconnected,
  EOCConnectionStateConnecting,
  EOCConnectionStateConnected,
};

定义选项的时候,若这些选项可以彼此组合,可以将各选项值定义为2的幂,以便通过按位或操作将其组合起来

enum UIViewAutoresizing {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 1,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 2,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 3,
    
}

注意:在处理枚举类型的switch语句中不要实现default分支,这样的话加入新枚举之后编译器就会提示开发者switch语句并未处理所有枚举,如果实现了default分支并处理了逻辑,加入新枚举之后则可能出现bug

第二章 对象、消息、运行期

第6条 理解 “属性” 这一概念

“属性”的作用:

为对象生成实例变量,并为实例变量自动生成set和get方法

“属性”特质:

  • 原子性
    • nonatomic 不使用同步锁
    • atomic 使用同步锁
  • 读/写权限
    • readwrite 同时存在存取方法
    • readonly 只有获取方法
  • 内存管理语义
    • assign 纯量类型(scalar type)的简单赋值操作
    • strong 拥有关系,保留新值,释放旧值,再设置新值
    • weak 非拥有关系,属性所指的对象遭到摧毁时,属性也会清空
    • unsafe_unretained 莱斯assign 适用对象类型,非拥有关系,属性所指的对象遭到摧毁时,属性不会清空
    • copy 不保留新值,二十将其拷贝
  • set、get 方法

注意 @dynamic关键字会告诉编译器不要自动创建实现属性所用的实例变量,也不要为其创建set、get 方法

如果属性定义为copy 那么在费设置方法里定义属性的时候,也要遵循copy的语义

第7条 在对象内部尽量直接访问实例变量

我们应该在读取实例变量时采用直接访问的形式,设置实例变量时通过属性来做

因为直接访问实例变量的优点是快速 直接 不会执行 set、get方法

而通过属性访问实例变量的优点是可以执行set、get方法,方便调试,并且可以触发KVO

需要注意的是:

  • 在初始化方法中有时候应该通过直接访问实例对象的方式,因为子类可能会重写某些属性的set方法,在子类初始化中通过属性访问可能出现问题;而有时候又必须通过属性访问,比如实例变量声明在父类中,在子类中是无法直接访问的只能通过属性访问
  • 在某个实例变量是由懒加载生成时,必须通过get方法来获取属性,否则不会触发懒加载方法无法生成属性。

第8条 理解“对象等同性”这一概念

按照==操作符比较出来的结果未必是我们先要的,因为该操作符比较的是两个指针本身,而不是其所指的对象,应使用NSObject协议中声明的“isEqual”方法

某些类有自己独有的等同性判断方法 比如NSString 的isEqualToString,NSArray 的isEqualToArray方法,NSDictionary的isEqualToDictionary方法

第9条 以 “类族模式” 隐藏实现细节

“类族” 会通过一个“抽象基类” 来实例化不同的子类,目的是为了隐藏实现细节

为什么这么做?

cocoa系统中大部分collection类都是类族

需要注意的是 通过类族模式创建出来的类 是子类 和基类并不是同一个类 调用isMemberOfClass方法会返回NO,调用isKindOfClass方法会返回YES

第10条 在既有类中使用关联对象存放自定义数据

可以使用“关联对象” 给系统已经存在的类关联其他的对象属性 ,关联对象方法提供了类似属性相应的“存储策略”,“内存管理意义” 等方法

但是此方法尽量不要滥用,有可能引入难于查找的bug

第11条 理解 objc_msgSend 作用

在Objective-C中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法,在底层,所有的方法都是普通的C语言函数,然后对象 收到消息之后,究竟该调用哪个方法完全取决于运行期决定,甚至可以在程序运行时改变,这些特性使得Objective-C成为真正的动态语言

id returnValue = [someObject messageName:parameter];

someObject 叫做“接收者”,messageName 叫做“选择器”,选择器与参数合起来称做“消息”,编译器会将消息转换为C语言函数调用,此行为所调用的函数是消息传递机制中的核心函数,叫做objc_msgSend

void objc_msgSend(id self, SEL cmd,...)

第一个参数代表接收者,第二个参数代表选择子,后续参数就是消息中的参数

上面的函数会转化为

id returnValue = objc_msgSend(someObject,@selector(messageName:),parameter);

此函数会在类中搜寻方法列表找到相应的方法去执行,如果没有就向上(父类)查找,一直没有就会执行“消息转发”,如果查找成功会将结果放在“快速映射表”中方便下次调用

第12条 理解消息转发机制

如果对象(以及其所有的父类)接收到无法解读的消息,就会启动“消息转发”机制,程序员可以进行处理(没有处理的话就会抛出方法无法找到的异常)

消息转发分为两个阶段

  1. 征询接受者,看它能否动态添加方法,以处理这个未知的选择子,这个过程叫做动态方法解析(dynamic method resolution)
  2. 请接收者看看有没有其它对象能处理这条消息
    • 2.1 如果有,则运行期系统会把消息转给那个对象
    • 2.2 如果没有,则启动完整的消息转发机制(full forwarding mechanism),运行期系统会把与消息有关的全部细节都封装到NSinvocation对象中,再给接受者最后一次机会,令其设法解决当前还未处理的这条消息

第13条 用“方法调配技术” 调试 “黑盒方法”

与选择子名称相对应的方法是可以在运行期被改变的,所以我们可以不用通过继承类并覆写方法就能改变这个类本身的功能

可以通过操纵类的方法列表中的IMP指针达到在运行期改变选择子对应的方法

在实际应用中一般会利用这个特性来给既有方法添加新功能,它的实现原理是:先通过分类增加一个新方法,然后将这个新方法和要增加功能的旧方法替换(旧方法名对应新方法的实现),这样一来我们调用旧方法的时候,就会执行新方法的实现

第14条 理解 “类对象” 的用意

在运行期程序的头文件里定义了描述OC对象所用的数据结构:

typedef struct objc_class *Class;
    struct objc_class {
         Class isa;
         Class super_class;
         const char *name;
         long version;
         long info;
         long instance_size;
         struct objc_ivar_list *ivars;
         struct objc_method_list **methodLists;
         struct objc_cache *cache;
         struct objc_protocol_list *protocols;
};

在这里,isa指针指向了对象所属的类:元类(mataclass),他是整个结构体的第一个变量。super_class定义了本类的超类

使用isMemberOfClass:能够判断出对象是否为某个特定类的实例;
使用isKindOfClass:能够判断出对象是否为某类或其派生类的实例

这两个方法都是利用了isa指针获取对象所属的类,然后通过super_class类在继承体系中查询

部分图片、代码、笔记源自J_Knight_的博客

相关文章

网友评论

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

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