本文并不是一篇完整的教程,更像一篇快速笔记,讲解 Objective-C 中的对象模型和消息传递。

Objective C

Objective-C 对象模型

Objective-C 对象的实质

Objective-C 中一切皆对象,如下的代码中,someString 是对象,NSString 是类对象。

NSString *someString = @"The string";

someString 对象会参照如下 id 定义的模版进行定义,其本质也是结构体指针:

typedef struct objc_object {
    Class isa; // 是什么类
} *id;

Class 定义是如下的类对象结构体指针:

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;
}

综合起来,类关系图如下:

Objective-C Class Hierarchy

由上图可见,类方法就是定义在 metaclass 中的实例方法。

检测类层级的方法:

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;

Objective-C 对象的内存分布

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

上面的代码在内存中的表示如下图:

NSString

栈中的变量在压栈出栈的时候被自动管理,堆中的内存管理被 Objective-C 做了抽象,不需要使用 开始学习 Objective-C - 动态存储分配 中描述的 C 语言的动态存储分配函数,而是 Objective-C 内存管理 中描述的自动引用计数 ARC 来管理。

Objective-C 消息传递

方法派遣

先来看一下 Objective-C 和 C++ 中的方法调用:

Object *obj = [Object new];
[obj performWith:parameter1 and:parameter2];
Object *obj = new Object();
obj->perform(parameter1, parameter2);

在 Objective-C 中,方法调用的本质是消息传递,都是在运行时决定那个实现代码被执行,编译器不关心被发送消息的对象类型,都是在运行时做决定。

而在 C++ 中,方法调用是在编译时决定的,Virtual Function 是通过 Virtual Table 在运行时决定那个实现代码被执行。

objc_msgSend

Object *obj = [Object new];
[obj performWith:parameter1 and:parameter2];

上面的方法调用,都会被编译器转化为 C 函数 objc_msgSend:

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

id 就是消息的接收者,SEL 就是对方法的封装,… 就是参数值

objc_msgSend 根据 id 和 SEL 找到正确的方法然后调用,objc_msgSend 会把这个查找结果缓存起来,下次就不用找了。

动态方法解析

接着上面的说,objc_msgSend 根据 id 和 SEL 没有找到正确的方法,就会进行如下 3 步的动态方法解析:

+ (BOOL)resolveInstanceMethod:(SEL)sel;
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;

Method Swizzling

Objective-C 中的类都有一个如下列表,表示方法名到方法实现的对应:

Method Swizzling Before

经过如下代码的转换,lowercaseString 和 uppercaseString 对应实现进行了交换:

Method originalMethod = class_getInstanceMethod([NSString string], @selector(lowercaseString));
Method swappedMethod = class_getInstanceMethod([NSString string], @selector(uppercaseString));

method_exchangeImplementations(originalMethod, swappedMethod);

Method Swizzling After