OC对象
对象
在 Objective-C 中,每一个对象都是某个类的实例,且这个对象的isa指针指向它所属的类。每一个类描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。每一个对象都可以接受消息,而对象能够接收的消息列表是保存在它所对应的类中。
在官方runtime(objc4-709)文件的NSObject.h中
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
在objc.h中
/// An opaque type that represents an Objective-C class
typedef struct objc_class *Class;
/// A pointer to an instance of a class
typedef struct objc_object *id;
runtime.h
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;
/* Use `Class` instead of `struct objc_class *` */
objc-runtime-old.h
struct objc_class : objc_object {
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
......
}
objc.h
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
objc-privite.h
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
......
}
NSObject包含Class类型(结构体)的isa成员变量,class(objc_class)结构体继承自objc_object结构体。OC对象的本质是objc_object类型的结构体。
类
Objective-C中的类本质上也是对象,称之为类对象,它是某个类的实例,这个类称之为元类(metaclass)。
为了设计上的完整,所有的元类的 isa 指针都会指向一个根元类 (root metaclass)。根元类 (root metaclass) 本身的 isa 指针指向自己,这样就行成了一个闭环。一个对象能够接收的消息列表是保存在它所对应的类中的。实例方法是保存在类中的,而类方法是保存在元类中的。
由于类方法的定义是保存在元类 (metaclass) 中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以,为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。
Category
因为对象在内存中的排布可以看成一个结构体,该结构体的大小并不能动态变化。所以无法在运行时动态给对象增加成员变量。 相对的,对象的方法定义都保存在类的可变区域中。Objective-C 2.0 并未在头文件中将实现暴露出来,但在 Objective-C 1.0 中,可以看到方法的定义列表是一个名为 methodLists的指针的指针。通过修改该指针指向的指针的值,就可以实现动态地为某一个类增加成员方法。这也是Category实现的原理。同时也说明了为什么Category只可为对象增加成员方法,却不能增加成员变量。
通过objc_setAssociatedObject 和 objc_getAssociatedObject方法可以变相地给对象增加成员变量,但由于实现机制不一样,所以并不是真正改变了对象的内存结构
Method Swizzling
Objective-C 提供了以下 API 来动态替换类方法或实例方法的实现:
- class_replaceMethod 替换类方法的定义
- method_exchangeImplementations 交换 2 个方法的实现
- method_setImplementation 设置 1 个方法的实现
从以上的区别我们可以总结出这 3 个 API 的使用场景:
- class_replaceMethod, 当需要替换的方法可能有不存在的情况时,可以考虑使用该方法。
- method_exchangeImplementations,当需要交换 2 个方法的实现时使用。
- method_setImplementation 最简单的用法,当仅仅需要为一个方法设置其实现方式时使