目录


1 Object Model 概述

1.1 Runtime 3种对象

objc runtime有3种对象。

  1. instance:类型为id,即指向objc_object的指针;objc_object只有1个值,即类型为Class的变量isa

  2. class:类型为Class,即指向objc_class的指针;objc_class继承自objc_object,因此也有类型为Class的变量isa,指向metaClassobjc_class还会存储类的实例变量表,实例方法表,协议表,实例大小等;另外objc_class还有1个Class类型的变量superClass,指向父类。root class(即NSObject)的superClass指向nil。

  3. metaClass:类型也为Class,存储了类变量,类方法;并且有1个Class类型的变量superClass,指向父类metaClass。root meta class的superClass指回root class(即NSObject)。其中classmetaClass都以单例存储在内存中。

1.2 instance的创建

1
2
3
size_t = size = cls->instanceSize();
id objc = (id) calloc(1,size);
obj.isa = (uintptr_t)cls;

分析:instanceSize是编译时决定。存储在class里;calloc分配1块连续的内存,内存中前4Byte存储了cls地址。

1.3 instance,class,metaClass对应例子

对应关系

2 题目

2.0 基础

1
2
3
4
5
6
7
8
9
10
11
12
13
//都指向Father类
[Father class]
[father class]

//指向参数的isa
Class cls = object_getClass(father);                //Father类
Class metaCls = object_getClass([Father class]);    //Father元类

BOOL res1 = [NSObject class] == [[NSObject class] class];               //1
BOOL res2 = [NSObject class] == [[[NSObject class] class] class];       //1
BOOL res3 = [NSObject class] == object_getClass([NSObject class]);      //0
BOOL res4 = object_getClass([NSObject class]) == object_getClass(object_getClass([NSObject class])); //1
BOOL res9 = [NSObject class] == class_getSuperclass(object_getClass([NSObject class]));//1

分析:[NSObject class]指向NSObject类,因此后面无论加多少个class还是指向NSObject类;但是object_getClass(id obj)返回的是isa变量,因此object_getClass([NSObject class])返回的是NSObject元类。结合Object Model很容易得出1,1,0,1,1的结论。

2.1 NSObject类方法输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//  NSObject+Sark.h

#import <Foundation/Foundation.h>
        
@interface NSObject (Sark)
+(void)say;
@end
        

//  NSObject+Sark.m

#import "NSObject+Sark.h"
        
@implementation NSObject (Sark)
-(void)say{
    NSLog(@"NSObject Sark instance method:say");
}
@end
        
[NSObject say]; //output: NSObject Sark instance method:say
[[NSObject new] say]; //编译通不过,因为-say没有在头文件申明

分析:类别扩展了NSObject的类方法+(void)say,但没有提供实现;在.m文件里提供了实例方法-(void)say的实现。当调用[NSObject say]的时候,NSObject的metaClass找不到vTable里对应的IMP,就通过继承链在父类里找,而我们上面介绍过NSObject的元类的父类指向了自己。因此就到NSObject类里找IMP,也就是实例方法-(void)say,找到IMP后调用就输出了NSObject Sark instance method:say

2.2 NSObject自省方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
BOOL res1_1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];    //1
BOOL res1_2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  //0
        
Class cls = object_getClass(father);
Class metaCls = object_getClass([Father class]);
    
BOOL res2_1 = [(id)[Father class] isKindOfClass:[Father class]];        //0
BOOL res2_2 = [(id)[Father class] isMemberOfClass:[Father class]];      //0
BOOL res2_3 = [(id)[Father class] isKindOfClass:metaCls];               //1
BOOL res2_4 = [(id)[Father class] isMemberOfClass:metaCls];             //1
BOOL res3_1 = [(id)father isKindOfClass:[Father class]];                //1
BOOL res3_2 = [(id)father isMemberOfClass:[Father class]];              //1


+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = class_getSuperclass(tcls)) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = class_getSuperclass(tcls)) {
        if (tcls == cls) return YES;
    }
    return NO;
}
        

分析:isKindOfClass的实例方法和类方法的实现是一样的,因为实例方法[self class]object_getClass((id)self)一样。isKindOfClass先返回isa,如果和cls 一致则返回YES,否则从superClassisa继续比较判断。因此res1_1中,NSObject元类不是NSObject类,然后从NSObject元类的superClass找,根据object model,又指回了NSObject 类,因此返回1。res2_1,res2_3,res3_1同理可得。而isMemberOfClass的实例方法和类方法的实现也是一样,与isKindOfClass不同的地方在于只比较一级isa,而不会不相等时沿着继承链继续比较。res1_2,res2_2,res2_4,res3_2的结果也就一目了然了。

2.3 self & super关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//  Son.h
#import <Foundation/Foundation.h>
#import "Father.h"
@interface Son : Father
-(void)print;
@end

//  Son.m
#import "Son.h"
@implementation Son
-(void)print{
    NSLog(@"%@",[self class]);  //Son
    NSLog(@"%@",[super class]); //Son
}
/*
msg_sendSuper2(struct objc_super *super, SEL op, ...);
struct objc_super { 
    id receiver; 
    Class current_class
}
*/

@end

分析:super不是1个参数,而是编译器的关键字,转换成msg_sendSuper2(struct objc_super *super, SEL op, ...),该方法跳过本类方法在父类中找对应IMP,其中struct objc_super { id receiver;Class current_class}receiver作为IMP的输入参数。就是说[super class]Father类里开始沿着继承链找class实例方法,找到后将self作为参数输入,因此两个输出都是Son。

2.4 What is an Object and its iVar in Memory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//  Father.h
@interface Father : NSObject
@property(nonatomic, copy) NSString *firstName;
-(void)speak;

//  Father.m
#import "Father.h"

@implementation Father
-(void)speak{
    NSLog(@"my name is :%@",self.firstName);
}
@end

//ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];
    
//  NSString *name = @"Jim";
    id cls = [Father class];
    void *ptr = &cls;
    [(__bridge id)ptr speak];
    //注释掉@"Jim",输出:  my name is: <ViewController: 0x7ae295d0>
    //插入@"Jim",  输出:  my name is: Jim
}

分析:这里是将instance分配在stack上,id结构体的地址和isa地址是同一块,因为isa是id结构体的第一个(也是唯一一个变量)。当调用[ptr speak]的时候,objc_sendMsg(id self, SEL sel)穿进去的self是分配在stack上的ptr,通过isa找到speakIMP后,调用了self.firstName,即ptr偏移4Byte。由于stack的地址是上面高,下面低,因此+4Byte就找到了name

如果name不在,则是viewController,一种说的通的解释如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//ViewController.m
- (void)viewDidLoad {
    // 以下数字越高表示地址越大
    // 压栈参数1: id self (4)
    // 压栈参数2: SEL _cmd (3)
    [super viewDidLoad]; // objc_msgSendSuper2(struct objc_super, SEL)
    // struct objc_super2 {
    //    self, (1)
    //    self.class (2)
    // }
    // objc_msgSendSuper2的SEL不需要申请栈空间

    id cls = [Sark class];
    void *obj = &cls; // (0)
    [(__bridge id)obj speak];
}

最后总结成

1
2
3
4
//objc中的对象是1个指向ClassObject地址的变量
id obj = &ClassObject
//而对象的实例变量
void *ivar = &obj+offset(N)

3 总结

Runtime_Object Model

4 Reference


Share Post

Twitter Google+

Shunmian

The only programmers in a position to see all the differences in power between the various languages are those who understand the most powerful one.