一 通用的一些东西
- OC 的面向对象是基于C/C++的结构体实现的。
2.OC代码转C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
1-3将OC代码转为C++代码.png
可能会遇到的错误&解决
报错
SDK "iphoneos" cannot be loacted
可尝试输入如下指令。
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/
1-4报错-01.png
1-5报错-01-解决.png
- 苹果源码下载地址 :tarball:原始码
http://opensource.apple.com/tarballs
4.LLDB指令的使用
1-6常用LLDB指令.png
eg1:p/x student
eg2: p/x student->isa
eg3: p/x 0x000000010046a840 & 0x00007ffffffffff8
//eg4:更具方法地址打印方法名
eg4:
// 先获取Method 的地址
IMP p1SetAgeIMP2 = [self.person1 methodForSelector:@selector(setAge:)];
NSLog(@"%p",p1SetAgeIMP2);
p (IMP) 0x1017626c4
//打印结果
(IMP) $0 = 0x00000001017626c4 (Foundation`_NSSetIntValueAndNotify)
eg5: bt 打印方法的调用堆栈。
eg6: si 一行一行的指向汇编指令 (step )
eg7: c 继续执行 continue
5.实时常看内存数据: Debug->Debug Workflow ---> View Memory --->在Address 输入框中输入内存地址(eg: 0x000000010046a840) 可查看内存中的数据。
- 调整编译顺序:Xcode--->Targets--->Build Phases ---->Compile Sources (这里面就是要编译的文件,挪动他们的位置就是调整他们的编译顺序, 这个在学Category中会用到。)
7.计算结构体占用的内存大小。
两条规则:1.内存对齐规则: 最大成员大小的整数倍。2.能放下就放,放不下就新开空间。
二 OC对象
主要就是围绕着这张图来进行的,纯理论性的东西,通过代码来加深记忆。这门语言就是这样设计的,按照人间的游戏规则来进行游戏。有些东西跟以前的认知不一样,说明这是个规定,就像以前学英语,解释不了的,语感,就是这样的。
2-1重要图.png
2-1 OC 对象的分类
OC 对象分三种(见 2-1重要图 )
- instance对象 (实例对象),主要存储内容:isa指针、成员变量的具体值
2.class对象(类对象):主要放的东西 :isa 指针、superclass指针、成员变量、属性、协议、对象方法
3.meta-class对象(元类对象),主要放的东西:isa指针、superclass指针、类方法
。
问题:OC 的方法是怎么调用的?
务必记住:
对象方法存放在:class对象(类对象)中
类方法存放在:meta-class对象(元类对象)中
[person personInstanceClass]; 对象调方法,本质就是发消息:
objc_msgSend(person,@selector(personInstanceClass))
1.对象方法 (-号方法)的调用流程: 通过instance对象(实例对象)的isa指针----->
找到class对象(类对象)-->找它里面存储的对象方法。
找到了就调用--->找不到--->在通过superclass找到父类的class对象(类对象)-->找他里面对象方法
有就调用,没有就继续找----> nil ---> unrecognized selector send to xxxxx奔溃。
2.类方法(+号方法)的调用流程: 通过类的superclass指针--->找到meta-class对象(元类对象)找到了就调用。找不到与1.的流程类似。
2-1-1 instance 对象
instance对象就是通过alloc 创建出来的对象。每次调用alloc方法都会产生新的instance对象。
NSObject *obj1 = [[NSObject alloc] init];
NSObject *obj2 = [[NSObject alloc] init];
obj1 和 obj2 是两个不同的对象,分别占据着不同的内存空间。
instance对象在内存中存储的信息包括:
1>.isa 指针。(目的:通过isa找到class对象'类对象' 调用对象方法)
2>.其他成员变量(准确的说应该是其他成员变量的具体值)
通过 将OC代码转为C++代码,instance对象‘实例对象’的结构大致如下. instance对象底层就是C/C++的结构体
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
struct NSObject_IMPL {
Class isa;
};
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
};
struct Student_IMPL {
struct Person_IMPL Person_IVARS;
int _no;
};
3>.问题:一个NSObject对象占用多少内存?一个 Person对象?一个Student对象?这里指的都是instance对象(实例对象)
两种计算方式
NSObject *obj1 = [[NSObject alloc] init];
NSLog(@"%zd--%zd",
class_getInstanceSize([NSObject class]),
malloc_size((__bridge const void *)obj1)
); // 8--16
//实际用到的内存-->参考计算结构体占用的内内存大小。
#import <objc/runtime.h>
class_getInstanceSize([NSObject class]) //8
//操作系统实际分配的内存
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj1) //16
2-1-2 class 对象 (类对象)
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1);
Class objectClass5 = object_getClass(object2);
NSLog(@"%p--%p--%p--%p--%p",objectClass1,objectClass2,objectClass3,objectClass4,objectClass5);
//0x7fffb1ebf140--0x7fffb1ebf140--0x7fffb1ebf140--0x7fffb1ebf0f0--0x7fffb1ebf0f0
1. objectClass1--- xxx5都是NSObject的类对象
2.他们是同一个类对象(内存地址都一样),每个类在内存中有且之后一个class对象(类对象)
3.class对象,类对象在内存中存储的信息主要包括
3-1. isa 指针
3-2. superclass指针
3-3. 类的属性信息(@peoperty)
3-4. 类的对象方法信息(instance method)
3-5. 类的协议信息 (protocol)
3-6. 类的成员变量信息 (ivars)
2-1-3 meta-class 对象 (元类对象)
//1>获取元类对象
object_getClass(对象),如果传进来的是instance对象返回的就是class对象。如果传的是class对象,返回的就是meta-class对象。
Class objectMetaClass2 = object_getClass([NSObject class]); //Runtime API
//2>判断是不是元类对象
NSLog(@"是不是元类对象:%@",class_isMetaClass(objectMetaClass2) ?@"是":@"不是");
//3> 每个类在内存中有且只有一个meta-class对象
//4> meta-class 对象和class对象的内存结构是一样的,但用途不一样,元类在内存中存储的信息主要包括
4-1> isa 指针
4-2>superclass 指针
4-3>类方法信息(class method)
//5> 注意
通过[[NSObject class] class] 获取的并不是元类对象。
Class testObject = [[NSObject class] class];
NSLog(@"%p 是不是元类对象: %@",testObject,class_isMetaClass(testObject) ? @"是":@"不是");
三 isa指针和superclass指针
3-1 isa指针
instance对象(实例对象)、class对象(类对象)、meta-class(元类对象)都有isa指针。
instance对象(实例对象)没有superclas指针
3-1isa指针.png
isa指针的细节问题: ISA_MASK
从64bit开始isa 需要进行一次位运算,才能计算出真实地址。
3-2 ISA_MASK2.png 3-3 ISA_MASK.png
3-2 superclass
3-4class的superclass指针.png 3-5meta-class的superclass指针.png3-2-1拐弯的superclass指针
3-6拐弯的superclass指针.png图中红色箭头标出来的superclass指针。
eg:1.有一个Person类继承至NSObject,Person 里面有一个 + (void)test。不写方法的实现。2.给NSObject添加一个分类。申明 + (void)test 但是不实现。只实现 - (void)test {} 。3.问:[Person test] 会不会奔溃,unrecognize selector send to xxxxx ?
// Person 类
@interface Person : NSObject
+ (void)test;
@end
@implementation Person
@end
//NSObject+test
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
//注意:实现的是 - 号方法,对象方法
- (void)test {
NSLog(@"- [NSObject test] %p-- %d",self,__LINE__);
}
@end
//main.m 中
[Person test];
[NSObject test];
//问:程序运行的结果?
控制台打印结果
- [NSObject test] 0x7fffb1ebf140-- 18
- [NSObject test] 0x7fffb1ebf140-- 18
//原因分析:见图3-6 拐弯的superclass指针
Person 的class对象(类对象) ---->通过isa 指针---->找到自己的meta-class对象(元类对象)-->
发现没有+ (void)test 方法的实现----->沿着superclass指针往上找到基类的meta-class
(元类对象) 因为Person直接继承自NSObject对象---->
发现基类里面也没有 + (void)test方法的实现。此时基类的meta-class(元类对象)沿着superclass-----> 找到了基类的类对象。找到了 - (void)test{ } 方法的实现。---->展开调用。这就是我认为的:拐弯的superclass指针
疑问:
这里最终的结果是:一个类对象调用了对象(- 号)方法的实现。
[Person test] OC对象调方法其实就是发消息。
objc-msgSend(Person, @select(test)) 他是更具方法名去找对应的方法的实现。这个时候他并不会去区分+、-号方法,也没办法区分。
从这一点,OC的面向对象是个假的面想对象,基于消息发送机制、基于runtime、基于isa、superclass实现的一个面向对象。颠覆以前的认知。
四 窥探 struct object_class的结构
因为到这一步,都是C++的东西,不懂,∴ 暂略。
五 本文的代码
本文的代码可以在github上查看。
github LowLayerTheoryNote
网友评论