-
1 . 编写的Objective-C代码,其底层都是由c/c++ 代码实现的。
OC语言 —> c/c++ 语言—>汇编语言—>机器语言
2 . oc的对象是基于 c/c++ 的结构体实现的。
3 . oc 转换成 c/c++ 的终端命令:
clang -rewrite-objc <源文件> -o <目标文件>
- 添加支持的平台
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc <源文件> -o <目标文件>
//eg::
//xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
// 模拟器 (i386) , 32bit (arm7) , 64bit(arm64)
- 4 . NSObject 底层实现 转换成 c++
@interface NSObject{
Class isa;
}
@end
//转化 ::IMPL ->implementation
struct NSObject_IMPL {
Class isa;
};
// Class -> typedef struct objc_class *Class; 类指针
- 一个NSObject 对象占用多少内存?
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject * obj = [NSObject new];
// 引入头文件 #import <objc/runtime.h>
NSLog(@"NSObject class 实例对象成员变量(isa)所占用的大小>> %zu", class_getInstanceSize([obj class]));
//引入头文件 #import <malloc/malloc.h>
NSLog(@"obj 指针所指向内存的大小>> %zu",malloc_size((__bridge const void *)(obj)));
}
return 0;
}
/*结果
NSObject class 实例对象的大小>> 8
obj 指针所指向内存的大小>> 16
*/
/*
(64bit 环境)
一个类对象在创建时 , 系统分配了 16 个字节 ,内部成员变量 isa 占用了 8 个字节
*/
- 5 . 增加成员变量 ,对象占用内存增加
@interface Student : NSObjec
{
@public
int _age;
int _num;
}
@end
@implementation Student
@end
// clang 64bit 得到 Student_IMPL
struct NSObject_IMPL {
Class isa;
};
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
int _num;
};
// 探测内存占用 和结果
/*
Student class 实例对象的大小:: 16
stu 指针所指向内存的大小:: 16
*/
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student * stu = [Student new];
NSLog(@"Student class 实例对象的大小:: %zu", class_getInstanceSize([stu class]));
NSLog(@"stu 指针所指向内存的大小:: %zu",malloc_size((__bridge const void *)(stu)));
}
return 0;
}
/*
新增成员变量,会占用内存
*/
- 6 .类的继承 (结构体嵌套)
@interface Student : NSObject
{
@public
int _age;
int _num;
}
@end
@implementation Student
@end
@interface GraduateStudent : Student
{
@public
int _score;
}
@end
@implementation GraduateStudent
@end
// clang 结构体的嵌套结构
struct NSObject_IMPL {
Class isa;
};
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
int _num;
};
struct GraduateStudent_IMPL {
struct Student_IMPL Student_IVARS;
int _score;
};
// 探测内存占用 和结果
/*
Student class 实例对象的大小:: 16
stu 指针所指向内存的大小:: 16
GraduateStudent class 实例对象的大小:: 24
gStu 指针所指向内存的大小:: 32
注释 :: iOS 内部 16位内存对齐(解决 24 32 的疑惑);
*/
int main(int argc, const char * argv[]) {
@autoreleasepool {
//class_getInstanceSize 返回内存对齐后的占用空间
Student * stu = [Student new];
NSLog(@"Student class 实例对象的大小:: %zu", class_getInstanceSize([stu class]));
NSLog(@"stu 指针所指向内存的大小:: %zu",malloc_size((__bridge const void *)(stu)));
GraduateStudent *gStu = [GraduateStudent new];
NSLog(@"GraduateStudent class 实例对象的大小:: %zu", class_getInstanceSize([gStu class]));
NSLog(@"gStu 指针所指向内存的大小:: %zu",malloc_size((__bridge const void *)(stu)));
}
return 0;
}
/*
结构体内存对齐 三原则::
* 1 内存自然对齐,结构体的总大小,必须是其内部最大成员的整数倍,不足的需要补齐;
* 2 结构体或union联合的数据成员,第一个数据成员是要放在offset == 0的地方,
如果遇上子成员,要根据子成员的类型存放在对应的整数倍的地址上;
* 3 如果结构体作为成员,则要找到这个结构体中的最大元素,然后从这个最大成员的整数倍地址开始存储
eg:(strutc a中有一个struct b,b里面有char,int,double….那b应该从double的整数倍开始存储);
为何要内存对齐? (百度复制 666………… )
1 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;
某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
*/
-
7 . 对象方法
oc类的实例对象是以结构体实现的,同时这个结构体中包含了对象的实例变量,但是对象的方法并没有在结构体中体现
(因为类对象可以创建多个,但是方法实现只需一份就足够了) 8 .对象的分类
a . 实例对象 (instance 对象)
/*
a . 实例对象 (instance 对象)
通过类 alloc 出来的对象,每次alloc 都会产生新的对象,新的对象会开辟占据新的内存;
实例对象在内存中存储的信息包括:
isa 指针(继承NSObject)、
成员变量(ivar 这里保存成员变量的值)
*/
Student * student_0 = [[Student alloc] init];
Student * student_1 = [[Student alloc] init];
// student_0,student_1是Student实例对象;
b . 类对象(class 对象 )
/*
b . 类对象(class 对象 )
类产生的不同的实例对象指向同一个类对象;
每个类在内存中有且只有一个类对象;
类对象字在内存中存储的信息主要包括:
isa 指针、
superclass 指针 、
类的属性信息(@property)、
类的对象方法信息 (instance method(减号方法))、
类的协议信息(protocal)、
类的成员变量信息 (ivar 这里保存成员变量的类型,别名等)
……
*/
Student * student_0 = [[Student alloc] init];
Class student_0_class_0 = [student_0 class];
Class student_0_class_1 = [Student class];
// 引入头文件 #import <objc/runtime.h> 传入对象
Class student_0_class_2 = object_getClass(student_0);
NSLog(@" %p :: %p :: %p",student_0_class_0,student_0_class_1,student_0_class_2);
//打印结果(类唯一的类对象) 0x100001308 :: 0x100001308 :: 0x100001308
c . 元类对象 (meta - class 对象)
c . 元类对象 (meta - class 对象)
/*
每个类在内存中只有一个元类对象
元类对象和类对象的内存结构是一样的,但是用途不一样;
元类对象在内存中存储的信息主要包括::
isa指针、
superclass 指针、
类方法信息(加号方法)
……
*/
Student * student_0 = [[Student alloc] init];
Class student_0_class_0 = [student_0 class];
Class student_0_class_1 = [Student class];
Class student_0_class_2 = object_getClass(student_0);
// 元类对象 传入类对象
Class student_0_mateClass = object_getClass([Student class]);
NSLog(@" %p :: %p :: %p %p",student_0_class_0,student_0_class_1,student_0_class_2,student_0_mateClass);
//打印结果(类对象,元类对象) 0x100001308 :: 0x100001308 :: 0x100001308 0x1000012e0
/*
注释::[ [NSObject class] class] 返回的是类对象,并不是元类对象
判断是否是元类对象
Bool isMateClass = class_isMetaClass(student_0_mateClass)
*/
注释 :这两个方法是不同的
Class objc_getClass (const char * aClassName)
传入字符串 返回类对象
Class object_getClass (id obj)
传入实例对象 返回类对象
传入类对象 返回元类对象
传入元类对象 返回 NSObject(基类)的元类对象
- 9 .类的关系(百度借图……)
指向关系
/*
1. 实例对象的isa 指向类对象;
2. 类对象的isa 指向 元类对象
3.元类对象的isa 指向 NSObject(基类)类对象
4.类对象的superclass 指向父类的类对象(直到 NSObject(基类)类对象(NSObject 类对象的superclass nil))
5.元类对象的superclass 指向 父类的元类对象(直到 NSObject(基类)元类对象)
6基类的元类对象superclass 指向基类的类对象
*/
通过解读指向关系,查阅 instance 调用对象方法的轨迹(objc_msgSend)
obj[实例对象] 执行 instance_method 的过程::
1. 通过 obj[实例对象] 的 isa 找到自己的 obj[类对象],
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 继续 2 )
2.通过 obj[类对象] 的 superclass 找到 obj[父类_类对象];
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 继续 3 )
3. 通过 obj[父类_类对象] 的 superclass 找到 下一个父类类对象;
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 继续 4 )
4. 知道通过superclass 找到基类类对象,
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 报错 : unrecognize instance "instance_method" )
类 ClassA 执行 class_method 的调用轨迹
ClassA[类对象] 执行 class_method 的过程::
1. 通过 ClassA[类对象] 的 isa 找到自己的 ClassA[元类对象],
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 继续 2 )
2.通过 ClassA[元类对象] 的 superclass 找到 ClassA[父类_元类对象];
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 继续 3 )
3. 通过 ClassA[父类_元类对象] 的 superclass 找到 下一个父类类对象;
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 继续 4 )
4. 知道通过superclass 找到基类的元类对象,
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 继续 5 )
*重要* 5. 基类的元类对象通过superclass 找到基类的类对象(查找基类类对象的对象方法,
查看方法列表中是否包含调用方法;
(包含 -> 直接执行方法 ;不包含 -> 报错 : unrecognize method "class_method" )
原因::方法执行的本质是 objc_msgSend(参数) 方法,本身并不会区分 类方法 对象方法。
- 10 . isa 指针,superclass 指针
isa::
类对象的 isa -> 实例对象 isa 的值 & 指定环境的ISA_MASK
元类对象的 isa -> 类对象 isa 的值 & 指定环境的ISA_MASK
superclass::
子类类对象的superclass -> 父类类对象 地址值
子类元类对象的superclass -> 父类元类对象 地址值