OC 底层原理随问随答

161 阅读4分钟

1,一个oc实例对象占用多少内存空间

答:一个实例对象占用空间大小由其内部实例变量来决定的。NSObject实例对象由于没有其他内部变量,
所以只占用8个字节(isa指针,64位). 一个MyPerson对象继承NSObject,有 int _age, int _num两个变量,那么应当占用16个字节。假如,只用一个实例变量 int _age; 也是占用16个字节(实例对象
实际上是c++结构体,需要内存对齐)。 实际占用多大空间可以用方法class_getInstanceSize`来获取。但是这只是实例占用空间的大小,并不是实际分配空间的大小,要获取实际分配空间的大小需要使用`malloc_size`方法。另外alloc内部调用calloc分配空间最低大小为16个字节,所以[NSObject alloc]这里会分配16个字节而不是8个。并且alloc内部调用callloc分配空间都是以16字节的倍数来分配空间的,假如MyPerson 中有三个int类型的字段。那么实际占用空间应该是24个字节,而实际分配空间应该是32个字节(2倍的16)
2,oc中有几种类型的对象,这几种对象内部分别存储什么内容
有三种:1,实例对象。2,类对象,3元类对象
实例对象
1. 存储isa指针(指向类对象),对象成员变量的字段值(注意:只存储值)
类对象
1. isa指针(指向元类对象)
2. superClass指针(指向父类类对象,如果是NSObject那么 superClass指针指向null)
3. 属性信息,property的信息。如,属性名,类型等。但是不存储属性的具体值。
4. 实例方法信息,包括参数名,方法名,返回类型等信息
5. 类的协议protocol信息
6. 类的成员变量信息,包括类名,类型等,但是不存储变量的值
元类对象
1. isa指针(指向根元类,rootmetaclass)
2. superClass指针,指向父元类,根元类的superClass指针指向对应的类对象。
3. 类方法信息,包含方法指针,方法名,参数类型,方法实现等
4. 版本信息
5,协议列表,主要是用来自省,
6,方法缓存,存储最近用到的方法,方便快速查找

3,为什么方法会存储在类对象和元类对象中,而不是存储在实例对象中。

实例对象中仅仅存储成员变量的值,因为不同的实例对象的成员变量值是不同的,而对应的方法实现和签名确实是相同的,所以只需要存储一份。来节省内存空间

4,如果NSObject中添加分类,实现了一个实例方法- void test(); 那么MyObject继承于NSObject,[MyObject test]调用类方法的方式调用test,是否能调用成功,为什么?

可以,原因:oc中的方法调用实际上是objc_msgsend发送消息,参数位@selector(test)是无法区分是类方法还是实例方法的。其调用过程是,1,查找MyObject的isa指针找到其类对象,然后通过类对象的isa指针找到元类对象isa指针,查找对应的test类方法,没有找到,然后通过superClass指针一层层的往上查找,到了基类的元类中查找依然没有找到,会继续查找superclass指针对应的基类的类对象,也就是NSObject类对象,这里查到了实例方法test,进行调用。

5,NSObject的实例对象中的isa指针存储的地址就是NSObject类对象的地址吗? 为什么?

不是,原因:从64位开始isa指针存储的地址不再是对应的对象的地址。而是需要与runtime中的宏定义:ISA_MASK 进行按位与。需要进行转换。

6,KVO的原理

kvo 是对一个实例对象的属性值进行观察,当属性值发生变化时,会触发回调方法,以达到变更通知的目的,所以如果没有set方法的字段无法被观察
原理:通过对被观察对象例如:MyPerson对像的age属性添加观察者,在运行时中会为动态创建一个MyPerson的子类,并且重新相关方法:-(Class)class(), -(void)setAge:, -(void)dealloc(), -(Bool)_isKVOA,等方法。一般子类命名规则为NSKVONotifying_MyPerson. 
实例对象的isa指针指向NSKVONotifying_MyPerson类对象,而NSKVONotifying_MyPerson类对象的isa指针指向NSKVONotifying_MyPerson的元类对象。
setAge:重写内容为:在其中加入_NSSetIntValuesKeyAndNotify方法调用,此方法为foundation库中c方法。它会在值改变前后调用willChangeValueforPath,didChangeValueforPath。

7,利用运行时方法动态创建类,并创建相应的实例

#import <objc/runtime.h>
#import <Foundation/Foundation.h>

// 方法实现
void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@"Dynamic method called!");
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 1. 分配一个新的类对象
        Class newClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);
        
        // 2. 为新类添加方法
        class_addMethod(newClass, @selector(dynamicMethod), (IMP)dynamicMethodIMP, "v@:");
        
        // 3. 注册新类
        objc_registerClassPair(newClass);
        
        // 4. 创建新类的实例对象
        id instance = [[newClass alloc] init];
        
        // 5. 调用新类的方法
        [instance performSelector:@selector(dynamicMethod)];
    }
    return 0;
}