手把手带你去分析NSObject本质是什么

简介: 这篇行文主要讲 NSObject 对象三连问:NSObject本质是什么,占用多少内存,以及怎么去分析。从下图我们可以看到,我们平时编写的 Objective-C 代码,最终转换到机器语言的过程:https://img-blog.csdnimg.cn/2a6a930a8d3546f896d7527a95c3a576.png换句话说,Objective-C的面向对象都是基于C\C++的数据结构实现的,或者说其底层实现其实都是C\C++代码。

一、代码转换


1、 添加 NSObject 对象

如在 main.m 添加一个 NSObject 对象(或者直接复制以下代码到 main.m 文件中):


#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
    }
    return 0;
}


2、命令行实现

已知:

1、不同平台支持的代码是不一样的( Windows、mac、iOS)

2、iOS架构:模拟器(i386)、32bit(armv7)、64bit(arm64)


命令行格式如下:


// 不指定平台和架构
clang -rewrite-objc main.m -o main.cpp
// 指定真机(ios平台64bit架构)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
// 指定模拟器
xcrun -sdk iphonesimulator clang -arch i386 -rewrite-objc  main.m -o main-i386-simulator.cpp
// 命令行格式
// xcrun:xcode run
// -sdk 平台:iphoneos
// -arch 架构:arm64
// -rewrite-objc:重写OC
// main.m -o main-arm64-1.cpp:OC源文件  -o  输出的CPP文件
// 如果需要链接其他框架,使用-framework参数。比如-framework UIKit
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp


执行生成目录:


chenzimin@rattanchen InterView % tree
.
├── Info.plist
├── main-arm64-iphoneos.cpp
├── main-i386-simulator.cpp
├── main.cpp
└── main.m
0 directories, 5 files


3、结果

main 方法中转化的代码依次如下:

1、不指定机型架构:

dd3438357fb7458ba858730b52770c38.png

2、指定机型架构:

e0b471c205eb4253aacf31546f2216bf.png

以上直观的感知转cpp的代码是一样的,但是不配置机型机构的部分,因为需要涉及兼容问题,代码行数相差了将近三倍。所以按机型配置可以节省很多存储空间。


二、本质分析


看到OC实现和 C\C++ 底层实现,如下:

03fbf95dd7fe4a27ab6ce7b00cb4639f.png



结论:

1、NSObject 对象在内存中就是一个结构体(结构体优势:可以兼容不同种数据类型)。

4d1c5a48e54241beb008f2857a647843.png

2、Class:是指向结构体的指针。

3、指针占位大小:64位环境占8个字节(下面默认按64位系统计算),32位环境占4个字节。

4、而 NSObject_IMPL 只有一个成员对象,那么 isa 成员的地址就是结构体在内存中的地址,占8个字节。


NSObject *obj = [[NSObject alloc] init]; 代码解读:


// 1、创建好一个对象并给这个对象分配内存地址,假设地址为:0x100400110
[[NSObject alloc] init];
// 2、设置一个指针 *obj
NSObject *obj;
// 3、让 *obj 指针指向/存储这个对象的内存地址。
NSObject *obj = [[NSObject alloc] init];


图如下:

8bd77a9adbda47838106f3def6a8f897.png


三、NSObject 是有多少内存呢?


继续以上例子,已知有两个方法可以获取内存分配空间:


#import <objc/runtime.h>
#import <malloc/malloc.h>
// 获得NSObject实例对象的成员变量所占用的大小 >> 8
NSLog(@"%zd", class_getInstanceSize([NSObject class]));
// 获得obj指针所指向内存的大小 >> 16
NSLog(@"%zd", malloc_size((__bridge const void *)obj));


为什么一个返回8字节,另一个返回16字节。注意了,它们虽然都是获取占用内存大小。但是获取对象不一样。


class_getInstanceSize 方法获取的是实例对象的成员变量所占用的大小。

malloc_size 获取的是指针所指向的内存大小。

那为什么指针所指向的内存会更大呢?这时可以查看一下OC底层原码是如何分配内存的,如下:


 

size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }


可以看到,当分配内存小于16位时,直接设置16,这是他们底层的规定。


结论:

1、系统分配了16个字节给 NSObject 对象(通过malloc_size函数获得)

2、但 NSObject 对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)


四、Demo


InterView-NSObject的内存分析


相关文章
|
设计模式 弹性计算 人工智能
阿里技术专家详解DDD系列 第四讲 - 领域层设计规范
在一个DDD架构设计中,领域层的设计合理性会直接影响整个架构的代码结构以及应用层、基础设施层的设计。但是领域层设计又是有挑战的任务,特别是在一个业务逻辑相对复杂应用中,每一个业务规则是应该放在Entity、ValueObject 还是 DomainService是值得用心思考的,既要避免未来的扩展性差,又要确保不会过度设计导致复杂性。
|
Java 编译器 数据库连接
Cause java.sql.SQLDataException Unsupported conversion from LONG to java.sql.Timestamp
Cause java.sql.SQLDataException Unsupported conversion from LONG to java.sql.Timestamp
1341 0
|
存储 NoSQL Java
分布式session-SpringSession的应用
Spring Session 提供了一种创建和管理 Servlet HttpSession 的方案,默认使用外置 Redis 存储 Session 数据,解决了 Session 共享问题。其特性包括:API 及实现用于管理用户会话、以应用容器中性方式替换 HttpSession、简化集群会话支持、管理单个浏览器实例中的多个用户会话以及通过 headers 提供会话 ID 以使用 RESTful API。Spring Session 通过 SessionRepositoryFilter 实现,拦截请求并转换 request 和 response 对象,从而实现 Session 的创建与管理。
294 0
分布式session-SpringSession的应用
|
存储 Kubernetes 监控
Kubernetes集群管理的最佳实践
【7月更文挑战第25天】遵循上述最佳实践可以显著提升Kubernetes集群的管理效率、安全性和性能。然而,每个集群的环境和需求都是独特的,因此在实际操作中还需根据具体情况进行调整和优化。希望本文能为你管理Kubernetes集群提供一些有益的参考。
|
Kubernetes 监控 API
在K8S中,什么是静态Pod、自主式Pod、动态Pod?
在K8S中,什么是静态Pod、自主式Pod、动态Pod?
|
关系型数据库 MySQL Java
MySQL 巨坑:update 更新慎用影响行数做判断!!!
MySQL 巨坑:update 更新慎用影响行数做判断!!!
2019 0
MySQL 巨坑:update 更新慎用影响行数做判断!!!
|
NoSQL 关系型数据库 MySQL
图床项目详解-1
图床项目详解
406 0
|
数据采集 存储 Rust
Rust高级爬虫:如何利用Rust抓取精美图片
Rust高级爬虫:如何利用Rust抓取精美图片
|
存储 缓存 关系型数据库
认真学习数据库缓冲池(buffer pool)
认真学习数据库缓冲池(buffer pool)
476 0