EffectiveObjective-C2.0 笔记 - 第二部

2018-10-10  本文已影响0人  jrw7878

2.1 属性

1. "对象"(object)就是 "基本构造单元"(building block),开发者可以通过对象来存储并传递数据。

在对象直接传递数据并执行任务的过程就叫做 "消息传递"(Messaging)。

2. 程序运行起来后,为其提供相关支持的代码叫做"运行期环境"(runtime),它提供一些使得对象之间能够传递消息的重要函数。

理解运行期环境,可以帮你写出高效且易维护的代码。

3. Oc编译采用“应用程序二进制接口”(Application Binary Interface,ABI)

把实例变量当作一种存储偏移量所用的 “特殊变量”(speacial variable),交由 “类对象”(class object)保管。偏移量会在运行期查找,这样子总能找到正确的偏移量,这是稳固。

如果对象布局在编译器就固定了,访问变量时,编译器会使用 “偏移量”(offset)来计算,这个偏移量是 “硬编码”(hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。 存在一个问题:如果代码使用了编译期计算出来的偏移量,那么修改类定义之后必须重新编译,否则就会出错。

@property

1. 用于声明属性,自动添加实例变量,以下划线开头,自动实现属性的读写方法。

2. 在实现文件中可以通过@synthesize 语法来指定实例变量的名字

@implementation EOCPerson
@synthesize name = _myName;
@end

3. @dynamic 关键字会告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法

属性特质

原子性、读写权限、内存管理语义、方法名、其他。

1、原子性

2、读写权限

3、内存管理

MRC时,有assign、retain、copy,ARC加入了strong、weak

4、方法名

5、其他

nonnull, null_resettable, nullable

2.2 对象访问

1. 对象访问有两种、一种是实例访问、一种是属性的读写方法访问。

2. 具体情况应该根据他们的特点来定:

-(EOCBrain *)brain{
    if(!_brain){
        _brain = [EOCBrain new];
    }
    
    return _brain;
}

2.3 对象同等性

1. == 与 isEqual

2. NSObject 协议中有两个用于判断等同性的关键方法:

- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;

hash方法实现的一些情况:

// 1-固定值
- (NSUInteger)hash {
    return 12312312;
}
// 这种会对collection使用这个对象产生性能问题。因为在collection 在检索哈希表的时,会用对象的哈希码来做索引,在set 集合中,会根据哈希码把对象分装到不同的数组里面,在添加新对象的时候,要根据其哈希码找对与之对应的数组,依次检查其中各个元素,看数组已有的对象是否和将要添加的新对象相等,如果相等,就说明添加的对象已经在set 集合中了,是添加失败的。(如果所有对象的hash 值对一样,这样子set 集合只会有一个数组,所有数据都在一起了,每次插入数据都会遍历这个数组,这样子就会出现性能问题)

// 2-组合值
- (NSUInteger)hash {
    NSString *stringToHash = [NSString stringWithFormat@"%@:%@",_firstName,_lastNmae];
    return [stringToHash hash];
}
//这样子能在一定情况下保证返回不同的哈希码,但是这里会存在创建字符串的开销,会比返回单一值要慢

// 3-位运算
- (NSUInteger)hash {
    return [self.firstName hash] ^ [self.lastNmae hash];
}

// ^为逐位逻辑运算符,它表示逐位非或(如果只有一个位为1,那么结果为1;否则为0。)。
//这样子可以保存较高的效率,又不会过于频繁的重复

3. 特定类所具有的等同性判定方法

isEqualToString、isEqualToArray、isEqualToDictionary

4. 容器中可变类的等同性

2.4 类族模式

1. “类族” (class cluster)是一种很有用的模式(pattern),可以隐藏 “抽象基类” (abstract base class)背后的实现细节。

2. 用户无须自己创建子类实例,只需要调用基类方法来创建即可。

3. 如何创建类族

-(BOOL) isKindOfClass: classObj; //判断是否是这个类或者这个类的子类的实例
-(BOOL) isMemberOfClass: classObj; //判断是否是这个类的实例

2.5 关联对象存放自定义数据

1. 关联对象

可以给某个对象关联许多其他对象,这些对象通过“键”来区分。存储对象值的时候,可以指明“存储策略”,用以维护相应的“内存管理语义”。

OBJC_ASSOCIATION_ASSIGN --- assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC --- nonatomic, retain
OBJC_ASSOCIATION_COPY_NONATOMIC --- nonatomic, copy
OBJC_ASSOCIATION_RETAIN --- retain
OBJC_ASSOCIATION_COPY --- copy

下列方法可以管理关联对象:

void objc_setAssociatedObject (id object, void *key, id value, objc_AssociationPolicy policy)
// 此方法以给定的键和策略为某对象设置关联对象值

id objc_getAssociatedObject(id object, void *key) 
// 此方法根据给定的键从某个对象中获取相应的关联对象值

void objc_removeAssociatedObject(id object) 
// 此方法移除指定对象的全部关联对象

若想令两个健匹配到相同的一个值,则二者必须是完全相同的指针才行。所以,在设置关联对象值时:通常使用静态全局变量做键

2.6 消息

一. 消息传递(objc_msgSend)

1. 调用对象方法,在Objective-C 中叫做 “传递消息”(pass a message),消息有 “名称”(name)或“选择子”(selector),可以接受参数,而且可能还有返回值。

objc_megSend 的原型:

// 方法原型
// messageName 叫做 selector(选择子),选择子和参数合起来称为"消息"。
id returnValue = [receiveObject messageName:parameter];

// 所有方法都是普通的 C 语言函数,方法转为标准的 C 语言函数如下:
// 是一个 “参数个数可变的函数”,能够接受两个或两个以上的参数,
// 第一个参数代表接收者,第二个参数代表选择子,后续参数就是参数。
void objc_msgSend(id self,SEL cmd,...)

2. objc_megSend 函数会依据接收者和选择子来调用适当的方法:

3. 每个类里都有一张函数表,选择子的名称则是表的 “键”,对应的值都是指向函数的指针。objc_msgSend 等函数就是通过这个函数表来寻找应该执行的方法并执行跳转的。

4. objc_msgSend 会将匹配结果缓存在 “快速映射表”(fast map)里面,每个类都有这样子的一块缓存,接下来还向该类发送一样的消息,那么执行起来就很快了。

5. 这里有些特殊情况,需要由Objective-C 运行环境的另外一些函数来处理:

6. 如果某函数的最后一项操作是调用另外一个函数,那么就可以运用 “尾调用优化” 技术。编译器会生成跳转至另外一个函数所需的指令码,而且不会向调用栈推入新的 “栈帧”。

二、 消息转发

当对象接收到无法解读的消息后,就会启动 “消息转发”(message forwarding)机制,程序员可经由此过程告诉对象应该如何处理未知消息。

消息转发分为两大阶段:

1. 动态方法解析

第一阶段选征询接收者,所属的类,看其是否能动态添加方法,以处理当前这个 “未知的选择子“(unknown seletor),这叫做 ”动态方法解析“(dynamic method resolution)。

//表示这个类是否能新增一个方法来处理此选择子
+ (BOOL)resolveClassMethod:(SEL)sel
+ (BOOL)resolveInstanceMethod:(SEL)sel

2. 完整的消息转发机制

第二阶段涉及 ”完整的消息转发机制“(full forwarding mechanism)。

如果运行期系统已经把第一阶段执行完了,那么接收者自己就无法再以动态新增方法的手段来响应包含该选择子的消息了。这里的第二阶段又分为下面两小步:

- (id)forwardingTargetForSelector:(SEL)aSelector
- (void)forwardInvocation:(NSInvocation *)anInvocation

2.7 方法调配

1. 不需要源代码,也不需要通过继承子类来覆写方法就能改变这个类本身的功能,新功能在本类的所有实例都生效,此方案称为 “方法调配”(method swizzling)。

2. 每个类有个方法列表(函数指针 IMP),各自映射到自己的方法实现,只要我们能操作这个函数指针的指向,我们就可以动态的增加替换原有的方法。

3. 互换两个已经写好的方法实现:

// 获取方法实现:
Method class_getInstanceMethod(Class cls, SEL name)

// 调换方法实现
void method_exchangeImplementations(Method m1, Method m2) 

4. 为已有方法增加新功能:

    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
       method_exchangeImplementations(originalMethod, swizzledMethod);
    }

2.8 理解 “类对象” 的用意

1. 每个Objective-C 对象实例都是指向某块内存数据的指针。

2. Objective-C 对象所用的数据结构

struct objc_object {
    Class isa;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;

每个对象结构体首个成员是Class 类的变量,定义了对象所属的类,通常称为 “is a” 指针。

3. Class 对象的数据结构定义

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;

Class 首个变量也是isa 指针,说明Class 本身也是Objective-C 对象,指向 “元类”(meta class)。

4. 在类继承体系中查询类型信息

// 判断对象是否为某个特定类的实例,不能判定 super 类
isMemberOfClass

// 判断出对象是否为某类或其派生类的实例
isKindOfClass
上一篇 下一篇

猜你喜欢

热点阅读