OC 对象的本质
例:有Person对象 Person继承与NSObject ,有Student对象 Student继承Person。代码以及解析如下:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Person : NSObject
{
@public
int _age;
}
@end
@implementation Person
@end
@interface Student : Person
{
@public
int _no;
int _hight;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
/**
平时编写的 Objective - C 代码,底层实现其实都是C\C++代码
所以Objective- C的面向对象都是基于C\C++的数据结构实现的
Objective- C的对象、类主要是基于C\C++的结构体来实现的
所有的对象 苹果硬性规定 至少占用16个字节 (如果占用内存小于16 则直接返16个字节)
int 类型 4个字节
double 类型 8个字节
float 类型8个字节
string 类型8个字节
number 类型8个字节
生成c++文件命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc (要生成的文件) -o (生成后的文件)
iOS在分配内存的时候最后得到的都是16的倍数
*/
// 问题1 一个NSObject对象占用多少内存?
NSObject * obj = [[NSObject alloc]init];
//获得NSObject类的实例对象的成员变量所占用的存储空间(并不是真正占用内存的大小) :8
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
//获得obj指针所指向的内存的大小 :16
NSLog(@"%zd",malloc_size((__bridge const void *)(obj)));
// 问题2 Person对象占用多少内存?
Person *person = [[Person alloc]init];
person->_age = 10;
NSLog(@"%zd",malloc_size((__bridge const void *)(person)));
/**
答案 16
分析:Person 转化成C++是他的本质是一个结构体 如下所示:
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
struct NSObject_IMPL NSObject_IVARS; 又是一个指针指向NSObject 也是一个结构体 如下所示
struct NSObject_IMPL {
Class isa;
};
所以 Person 最终的结构体 可以理解为如下(这种方式不严谨)
struct Person_IMPL {
Class isa;
int _age;
};
所以占用内存为 8 + 4 = 12 但是由于 苹果硬性要求 一个对象最低占用16个字节 所以 Person对象占用内存为 16个字节
*/
// 问题3 Student对象占用多少内存?
Student *stu = [[Student alloc]init];
stu->_no = 20;
stu->_age = 30;
stu->_hight = 180;
NSLog(@"%zd",malloc_size((__bridge const void *)(stu)));
/**
答案 32
分析:Student 转化成C++是他的本质是一个结构体 如下所示:
struct Student_IMPL {
struct Person_IMPL Person_IVARS;
int _no;
};
struct Person_IMPL Person_IVARS; 也是一个结构体 如下所示
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
struct NSObject_IMPL NSObject_IVARS; 又是一个指针指向NSObject 也是一个结构体 如下所示
struct NSObject_IMPL {
Class isa;
};
所以 Student 最终的结构体 可以理解为如下(这种方式不严谨)
struct Person_IMPL {
Class isa;
int _age;
int _no;
int _hight;
};
所以占用内存为 8 + 4 + 4 + 4 = 20 但是iOS系统分配空间的时候是根据实际需要的大小来分配一块相近16倍数的空间 所以 Student对象占用内存为 32个字节 (证据来源于苹果底层源代码 cmalloc 函数)
*/
}
return 0;
}
1、一个NSObject对象占用多少内存?
系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
2、OC 对象分为几种?
答:OC对象分为三种,分别为 instance对象(实例对象)、class对象(类对象)、 meta-class对象(元类对象)
instance对象(实例对象)
通过alloc 创建的对象都是实例对象 如
NSObject *objc = [[NSObject alloc]init];
objc 就是实例对象
instance对象在内存中存储的信息主要包括
isa指针
其他成员变量
class对象(类对象)
获取类对象的方法有以下三种
Class objcClass1 = [objc class];
Class objcClass2 = [NSObject class];
Class objcClass3 = object_getClass(objc);
每个类在内存中有且只有一个class对象
class对象在内存中存储的信息主要包括:
isa指针
superclass指针
类的属性信息(@property)
类的对象方法信息(instance method)
类的协议信息(protocol)
类的成员变量信息(ivar)
meta-class对象(元类对象)
获取元类对象 直接将类对象传进去 就获得了元类对象
Class objectMetaClass = object_getClass ([NS0bject class]);
每个类在内存中有且只有一个meta-class对象
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中
存储的信息主要包括:
isa指针
superclass指针
类的类方法信息(class method。也就是+开头的方法)
注意:实例对象里面的成员变量和类对象的里面的成员变量是有区别的
实例对象的成员变量存放的是变量的值 比如 int a = 8 他存放的就是8 这个值
类对象的成员变量存放的是变量的信息 比如 变量的类型、变量的名称
原因是:一个对象可以创建很多实例对象,但是他有且只有一个类对象和一个元类对象
@interface Student : Person
{
@public
int _no;
int _hight;
}
@end
@implementation Student
@end
这个Person 可以创建很多实例变量如下所示:
Person *person1 = [[Person alloc]init];
Person *person2 = [[Person alloc]init];
Person *person3 = [[Person alloc]init];
...
person1、person2、person3 都是 Person类的实例对象 每个实例对象都有 _no和_hight的值 他们互不影响,所以在内存中存在多份。
但是 int _no、int _hight 这些变量的名称和类型永远只存在一份,所以存放在类对象中。
3、对象的isa指针指向哪里?流程如图所示: isa指针.png
instance(实例对象)对象的isa指向class对象
class(类对象)对象的isa指向meta-class对象
meta-class(元类)对象的isa指向基类的meta-class对象
4、OC的类信息存放在哪里?
成员变量的具体值,存放在instance对象
对象方法、属性、成员变量、协议信息,存放在class对象中
类方法,存放在meta-class对象中
5、isa、superclass总结
instance的isa指向class
class的isa指向meta-class
meta-class的isa指向基类的meta-class
class的superclass指向父类的class
如果没有父类,superclass指针为nil
meta-class的superclass指向父类的meta-class
基类的meta-class的superclass指向基类的class
instance调用对象方法的轨迹
isa找到class,方法不存在,就通过superclass找父类
class调用类方法的轨迹
isa找meta-class,方法不存在,就通过superclass找父类
isa和superclass指向流程.png
网友评论