美文网首页
笔记(一):OC对象本质

笔记(一):OC对象本质

作者: RBNote | 来源:发表于2018-07-21 16:07 被阅读15次
1-1OC对象本质.png 1-2总图.png

一 通用的一些东西

  1. 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
  1. 苹果源码下载地址 :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) 可查看内存中的数据。

  1. 调整编译顺序:Xcode--->Targets--->Build Phases ---->Compile Sources (这里面就是要编译的文件,挪动他们的位置就是调整他们的编译顺序, 这个在学Category中会用到。)
    7.计算结构体占用的内存大小。
    两条规则:1.内存对齐规则: 最大成员大小的整数倍。2.能放下就放,放不下就新开空间。
1-7结构体占用内存大小eg1.png 1-8结构体占用内存大小eg2.png 1-9结构体占用内存代大小eg3.png

二 OC对象

主要就是围绕着这张图来进行的,纯理论性的东西,通过代码来加深记忆。这门语言就是这样设计的,按照人间的游戏规则来进行游戏。有些东西跟以前的认知不一样,说明这是个规定,就像以前学英语,解释不了的,语感,就是这样的。


2-1重要图.png

2-1 OC 对象的分类

OC 对象分三种(见 2-1重要图 )

  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指针.png
3-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

相关文章

  • OC 与 Swift

    OC对象的本质(上):OC对象的底层实现原理OC对象的本质(中):OC对象的种类OC对象的本质(下):详解isa&...

  • OC对象的本质(中)—— OC对象的种类

    OC对象的本质(上):OC对象的底层实现原理OC对象的本质(中):OC对象的种类OC对象的本质(下):详解isa&...

  • OC对象的本质(下)—— 详解isa&supercl

    OC对象的本质(上):OC对象的底层实现原理OC对象的本质(中):OC对象的种类OC对象的本质(下):详解isa&...

  • 笔记(一):OC对象本质

    一 通用的一些东西 OC 的面向对象是基于C/C++的结构体实现的。 2.OC代码转C++代码 可能会遇到的错误&...

  • OC对象的本质(上)

    iOS | OC对象本质 | Objective-C 什么是OC语言,OC对象、类的本质是什么,OC对象的内存布局...

  • 内存管理--简单整理

    课堂笔记: 只有OC对象才需要进行内存管理的本质原因是OC对象存放在堆里面,非OC对象一般放在栈里面(栈内存会被系...

  • Runtime:OC对象、类、元类的本质

    零、Runtime是什么一、OC对象的本质二、OC类的本质三、OC元类的本质四、Runtime关于对象、类、元类的...

  • iOS底层isa结构分析

    在介绍正文之前,首先需要理解一个概念:OC对象的本质是什么? OC对象本质 在探索oc对象本质前,先了解一个编译器...

  • block 笔记

    block本质是OC对象(封装了函数调用以及调用环境的OC对象) 本质

  • OC对象原理探究(下)

    介绍正文前,我们思考一个问题,什么是对象?或者说OC对象的本质是什么? 对象本质以及拓展 在探索oc对象本质前,先...

网友评论

      本文标题:笔记(一):OC对象本质

      本文链接:https://www.haomeiwen.com/subject/mqbqmftx.html