iOS Principle:ClassAndObjective(上)

简介: iOS Principle:ClassAndObjective(上)

方便记忆:


  • OC 三种对象:instance实例对象、class类对象、meta-class元类对象
  • instance实例对象:NSObject转化为c语言其实就是一个结构体,系统分配内存空间,存放一个成员isa指针表示对象的地址(结构体的地址)64bit占用8个字节,32bit占用4个字节
  • class类对象:类对象内存存储的信息:isa和superclass指针、类的属性信息和成员变量、对象方法和协议信息
  • meta-class元类对象:元类对象和class对象的内存结构一样,isa指针指向基类对象,基类的元类对象的isa指针指向自己
  • OC 三种对象原理:objc_class结构体的指针


关于OC对象的底层实现


寻OC对象的本质,我们平时编写的Objective-C代码,底层实现其实都是C\C++代码。


image.png


OC的对象结构都是通过基础C\C++的结构体实现的。 我们通过创建OC文件及对象,并将OC文件转化为C++文件来探寻OC对象的本质

OC如下代码

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
        NSLog(@"Hello, World!");
    }
    return 0;
}

我们通过命令行将OC的mian.m文件转化为c++文件


clang -rewrite-objc main.m -o main.cpp // 这种方式没有指定架构例如arm64架构


我们可以指定架构模式的命令行,使用xcode工具 xcrun


xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp // 生成 main-arm64.cpp 


main-arm64.cpp 文件中搜索NSObjcet,可以找到 NSObjcet_IMPL(IMPL代表 implementation 实现)

我们看一下NSObject_IMPL内部


struct NSObject_IMPL {
    Class isa;
};
// 查看Class本质
typedef struct objc_class *Class;
// 我们发现Class其实就是一个指针,对象底层实现其实就是这个样子。

思考: 一个OC对象在内存中是如何布局的?


NSObjcet的底层实现,点击NSObjcet进入发现NSObject的内部实现


@interface NSObject <NSObject> {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
    #pragma clang diagnostic pop
}
@end


转化为c语言其实就是一个结构体


struct NSObject_IMPL {
    Class isa;
};

那么这个结构体占多大的内存空间呢,我们发现这个结构体只有一个成员,isa指针,而指针在64位架构中占8个字节。也就是说一个NSObjec对象所占用的内存是8个字节。


为了探寻OC对象在内存中如何体现,我们来看下面一段代码


NSObject *objc = [[NSObject alloc] init];


上面一段代码在内存中如何体现的呢?上述一段代码中系统为NSObject对象分配8个字节的内存空间,用来存放一个成员isa指针。那么isa指针这个变量的地址就是结构体的地址,也就是NSObjcet对象的地址。


假设isa的地址为0x100400110,那么上述代码分配存储空间给NSObject对象,然后将存储空间的地址赋值给objc指针。objc存储的就是isa的地址。objc指向内存中NSObject对象地址,即指向内存中的结构体,也就是isa的位置。


自定义类的内部实现

@interface Student : NSObject{
    @public
    int _no;
    int _age;
}
@end
@implementation Student
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student *stu = [[Student alloc] init];
        stu -> _no = 4;
        stu -> _age = 5;
        NSLog(@"%@",stu);
    }
    return 0;
}
@end


按照上述步骤同样生成c++文件。并查找Student,我们发现Student_IMPL


struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
};


发现第一个是 NSObject_IMPL的实现。而通过上面的实验我们知道NSObject_IMPL内部其实就是Class isa 那么我们假设 struct NSObject_IMPL NSObject_IVARS;


struct Student_IMPL {
    Class *isa;
    int _no;
    int _age;
};


因此此结构体占用多少存储空间,对象就占用多少存储空间。因此结构体占用的存储空间为,isa指针8个字节空间+int类型_no4个字节空间+int类型_age4个字节空间共16个字节空间


Student *stu = [[Student alloc] init];
stu -> _no = 4;
stu -> _age = 5;


那么上述代码实际上在内存中的体现为,创建Student对象首先会分配16个字节,存储3个东西,isa指针8个字节,4个字节的_no ,4个字节的_age


image.png


sutdent对象的3个变量分别有自己的地址。而stu指向isa指针的地址。因此stu的地址为0x100400110,stu对象在内存中占用16个字节的空间。并且经过赋值,_no里面存储着4 ,_age里面存储着5


验证Student在内存中模样

struct Student_IMPL {
    Class isa;
    int _no;
    int _age;
};
@interface Student : NSObject {
    @public
    int _no;
    int _age;
}
@end
@implementation Student
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 强制转化
        struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
        NSLog(@"_no = %d, _age = %d", stuImpl->_no, stuImpl->_age); // 打印出 _no = 4, _age = 5
    }
    return 0;
}


上述代码将oc对象强转成Student_IMPL的结构体。也就是说把一个指向oc对象的指针,指向这种结构体。由于我们之前猜想,对象在内存中的布局与结构体在内存中的布局相同,那么如果可以转化成功,说明我们的猜想正确。由此说明stu这个对象指向的内存确实是一个结构体。


实际上想要获取对象占用内存的大小,可以通过更便捷的运行时方法来获取。

class_getInstanceSize([Student class])
NSLog(@"%zd,%zd", class_getInstanceSize([NSObject class]) ,class_getInstanceSize([Student class]));
// 打印信息 8和16


窥探内存结构

实时查看内存数据

方式一:通过打断点。 Debug Workflow -> viewMemory address中输入stu的地址


image.png


从上图中,我们可以发现读取数据从高位数据开始读,查看前16位字节,每四个字节读出的数据为 16进制 0x0000004(4字节) 0x0000005(4字节)  isa的地址为 00D1081000001119(8字节)


方式二:通过lldb指令xcode自带的调试器

memory read 0x10074c450
// 简写  x 0x10074c450
// 增加读取条件
// memory read/数量格式字节数  内存地址
// 简写 x/数量格式字节数  内存地址
// 格式 x是16进制,f是浮点,d是10进制
// 字节大小   b:byte 1字节,h:half word 2字节,w:word 4字节,g:giant word 8字节
示例:x/4xw    //   /后面表示如何读取数据 w表示4个字节4个字节读取,x表示以16进制的方式读取数据,4则表示读取4次

同时也可以通过lldb修改内存中的值

memory write 0x100400c68 6
将_no的值改为了6


image.png


那么一个NSObject对象占用多少内存? NSObjcet实际上是只有一个名为isa的指针的结构体,因此占用一个指针变量所占用的内存空间大小,如果64bit占用8个字节,如果32bit占用4个字节。

目录
相关文章
|
iOS开发
iOS Principle:CGAffineTransform
iOS Principle:CGAffineTransform
145 0
iOS Principle:CGAffineTransform
|
安全 Unix API
iOS Principle:CALayer(下)
iOS Principle:CALayer(下)
138 0
iOS Principle:CALayer(下)
|
iOS开发
iOS Principle:CALayer(中)
iOS Principle:CALayer(中)
121 0
iOS Principle:CALayer(中)
|
API C语言 iOS开发
iOS Principle:CALayer(上)
iOS Principle:CALayer(上)
144 0
iOS Principle:CALayer(上)
|
存储 缓存 iOS开发
iOS Principle:weak
iOS Principle:weak
120 0
iOS Principle:weak
|
存储 iOS开发
iOS Principle:Notification(下)
iOS Principle:Notification(下)
90 0
iOS Principle:Notification(下)
|
设计模式 iOS开发
iOS Principle:Notification(上)
iOS Principle:Notification(上)
101 0
iOS Principle:Notification(上)
|
Web App开发 JSON 移动开发
iOS Principle:ReactNative(下)
iOS Principle:ReactNative(下)
155 0
iOS Principle:ReactNative(下)
|
移动开发 前端开发 JavaScript
iOS Principle:ReactNative(中)
iOS Principle:ReactNative(中)
99 0
iOS Principle:ReactNative(中)
|
移动开发 开发框架 自然语言处理
iOS Principle:ReactNative(上)
iOS Principle:ReactNative(上)
121 0