iOS 面试总结- 类的本质(一)

类的分类:

实例对象(instance)
类对象(class)
元类对象(meta-class)

问题:

1.类信息是存放在什么地方的呢?

实例对象:存放的具体对象的成员变量的值,
类对象中存放着:类的属性,成员变量,对象方法信息,协议信息,isa,superclass
元类对象中存放着:类方法信息,isa,superclass
每个类在内存中有且只有一个class对象(类对象),有且只有一个元类对象

[NSObject class]  //类对象的获取
Class meteClass = object_getClass([NSObject class]); //元类对象获取

2.一个NSObject对象占用多少内存?

在系统alloc的时候,会分配16个字节。(通过malloc_size函数验证)
但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数验证)
当小于16个字节时,系统也会默认开辟16个字节的空间用于存放对象。

3.每个对象中都有一个isa指针。这个指针的作用是什么呢?

instance对象的isa指向class对象
class对象的isa指向meta-class对象
meta-class对象的isa指向基类的meta-class对象
NSObject的元类isa指向自身、superclass指向NSObject的类对象。这样才能形成一个闭环。
文字太枯燥,show me the code

@interface JQApple : JQFruit
@end

@implementation JQApple
- (instancetype)init{
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
@end

转自https://www.jianshu.com/p/b1274e3e3768,里面有详解。

4.如何查看Class是否为meta-class
BOOL result  = class_isMetaClass([NSObject class]);

看到这里,我们大概已经了解了对象接收到消息时,oc中消息分发机制。

5.那么有个疑问哈,既然对象的消息响应要走这么长一段流程,那么对象响应性能岂不是很慢吗?但实际上oc中的响应效率是很高的。系统这里面是做的什么操作呢?

苹果底层用散列表缓存了方法,缓存过程中会通过方法名作为key,用指针地址&上一个mask(mask的值为散列表长度-1,这样保证了得出的索引值小于散列表的长度),从而求出索引值,根据索引值来存放方法实现。当想对象放松objc_send的时候,也是通过该方法获取索引值。这样通过一个位运算就可以获得方法的实现。

附:
消息分发机制过程:

1.
在Objective-C中,方法调用是一个消息发送的过程(在java,C++等静态语言中是函数调用)。
OC对象发送一个消息的过程如下:
1.向对象发送消息
2.在缓存中查找是否有匹配方法。如果有则响应,否则继续
3.在对象的类对象的方法列表中(method list)查找是否有匹配方法,如果有则响应,否则继续
4.在对象的父类的缓存中查找是否有匹配方法。如果有则响应,否则继续
5.在对象的父类的方法列表中(method list)查找是否有匹配方法。如果有则响应,否则继续
6.进入动态解析 -(BOOL)resolveInstanceMethod:(SEL)sel;- (BOOL)resolveClassMethod:(SEL)sel;查看是否有动态添加方法实现。如果有则响应,否则继续
7.进入消息重定向 -(id)forwardingTargetForSelector:(SEL)sel;如果有指定消息接收对象则响应,否则(指:返回nil或者self)继续
8.进入方法签名- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector返回方法签名并进入下一步,否则调用doesNotRecongnizeSelector:方法并抛出异常。
9.进入消息转发 - (void)forwardInvocation:(NSInvocation *)aInvocation;如果有指定消息转发对象则响应,否则调用doesNotRecongnizeSelector:方法并抛出异常。

我们思考个问题:

有person类和student类
class Person{
    +(void)test{
        nslog(@"123");
    }
}

class Student : Person{
}

当用一个Student对象调用test方法时,打印会执行么?
[[Student alloc] test];

推荐阅读更多精彩内容