主要内容:
1 Runtime数据结构
2 对象,类,元类之间关系
3 消息传递
4 消息转发
5 常见问题
数据结构
objc_object定义
truct objc_object {
private:
isa_t isa;
public:
//1 关联对象
//2 弱引用
//3 内存管理retain
提供一些对isa进行操作的方法
}
id 就是 objc_object
typedef struct objc_object *id;
isa概览
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
uintptr_t indexed : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
isa分为两种
指针型isa : 值代表Class地址
非指针型isa:值的部分代表Class地址,就是上面的shiftcls代表Class地址,indexed占用1bit表示是否使用优化的存储引用计数,has_assoc表示对象是否有关联对象,has_sidetable_rc是否使用了引用计数表,extra_rc存储引用计数减一后的结果。
objc_class定义
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
}
NSObject 都有一个Class类型的isa ,Class 就是 objc_class
@interface NSObject <NSObject> {
Class isa;
}
typedef struct objc_class *Class;
cache_t 结构
作用:用于快速查找方法执行函数
是一个哈希表结构,通过hash(key)来定位到bucket_t,提取imp.
列表结构
struct cache_t {
struct bucket_t *_buckets;
……
}
节点结构
struct bucket_t {
private:
cache_key_t _key;
IMP _imp;
……
}
class_data_bits_t
包含了属性、方法、协议,主要对class_rw_t封装,代表读和写。
struct class_rw_t {
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
……
}
class_rw_t中的methods、properties、protocols都是二维数组。第一个维度存的是分类如Person(China)、Person(America)。第二个维度存的才是具体的方法列表,成员列表,协议列表。还有一个比较重要的成员class_ro_t代表只读,存的是Person类的相关信息。
class_ro_t 结构
struct class_ro_t {
const char * name; //类名
method_list_t * baseMethodList; //一维数组
protocol_list_t * baseProtocols; //一维数组
property_list_t *baseProperties; //一维数组
const ivar_list_t * ivars; //一维数组
……
}
method_t
struct method_t {
SEL name;
const char *types;
IMP imp;
……
}
![](https://img.haomeiwen.com/i1658883/721a6183576f06ca.png)
![](https://img.haomeiwen.com/i1658883/66ba9bf758766bd3.png)
通过对objc_object和objc_class结构了解,我们可以串联出一幅整体构图
![](https://img.haomeiwen.com/i1658883/aa39bf3319a974c7.png)
对象、类对象、元类对象
三者关系:类对象存储实例方法列表等信息,元类对象存储类方法列表等信息
![](https://img.haomeiwen.com/i1658883/a348cf284db98d87.png)
实例方法查找:根据对象的isa找到类对象,逐级在类对象,父类对象,根类对象中查找。
类方法查找:根据类对象的isa找到元类对象,逐级在元类对象,父元类对象,根元类对象,根类对象中查找。
类方法没有实现,但根类中有同名的实例方法,此时会掉用根类的实例方法吗?
@interface UserInfo : NSObject
+ (id)copy;
+ (instancetype)init;
+ (void)finalize;
@end
@implementation UserInfo
@end
[UserInfo copy]; //正常运行
[UserInfo finalize]; //正常运行
[UserInfo init]; ///crash cannot init a class object.
self 实例对象
[self class] 类对象
object_getClass([self class]); 元类
self 类对象
[self class] 返回类自身
object_getClass([self class]); 元类
如何判断是否是元类
if(class_isMetaClass(obj)){
NSLog(@"obj 是元类");
}
消息传递
缓存查找:给定SEL,通过hash查找定位到bucket_t, 取出IMP。
当前类查找:已排序的,二分法。未排序的,遍历查找。
逐级父类查找:父类缓存查找,父类方法查找。
@interface Phone : Mobile
@end
__________________________________
@implementation Phone
-(id)init
{
self = [super init];
if(self){
NSLog(@"%@",NSStringFromClass([self class]));
NSLog(@"%@",NSStringFromClass([super class]));
}
}
@end
objc_msgSend(void /* id self, SEL op, ... */ )
objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class class;
__unsafe_unretained _Nonnull Class super_class;
};
[self class] = objc_msgSend(self,@selector(class));
[super class] = objc_msgSendSuper(super,@selector(class));
从super结构中可以看出,receiver还是当前类的对象self
class方法只在根类NSObject中才有,二者接收对象都是self.
所以[self class] 和[super class] 都会逐级向上查找,在根类中找到class的IMP,并进行调用,得到的结果都是Phone
消息转发
第一步: 动态添加
+(BOOL)resolveInstanceMethod:(SEL)sel
{
if(sel == @selector(test))
{
class_addMethod(self, @selector(test), goo, "v@:");
NSLog(@"第一次: resolveInstanceMethod");
return YES;
}else{
return [super resolveInstanceMethod:sel];
}
}
void goo(){
NSLog(@"goo");
}
第二步: 重定向
-(id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@"第二次: forwardingTargetForSelector");
return nil;
}
第三步:消息转发
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
{
if(aSelector == @selector(test))
{
NSLog(@"第三次 上: methodSignatureForSelector");
//v :void
//@:代表第一个参数是self
//: 代表第二个参数是SEL
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}else{
return [super methodSignatureForSelector:aSelector];
}
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"第三次 下:forwardInvocation:");
}
Method-Swlzzling
+(void)load
{
Method hello = class_getInstanceMethod(self, @selector(sayHello));
Method good = class_getInstanceMethod(self, @selector(sayGood));
method_exchangeImplementations(hello, good);
}
-(void)sayHello
{
NSLog(@"sayHello");
}
-(void)sayGood
{
NSLog(@"sayGood");
}
动态方法解析
@dynamic 运行时为方法添加get set
常见问题
1 编译时语言,动态语言区别
动态运行时语言将函数决议推迟到运行时
编译时语言在编译期进行函数决议
2 [obj foo]和objc_msgSend()关系
在编译后,转成objc_msgSend(self,@selector(foo));
3 runtime如何通过Selector找到对应的IMP地址?
当前类的缓存是否命中: hash查找
当前类的方法列表:已排序,二分法。未排序:遍历
父类逐级查找
4能否向编译后的类中添加实例变量?
编译后的类, 不能为它增加实例变量。class_ro_t 只读,不可写。
网友评论