NSObject的内存
clang -rewrite-objc main.m -o main.cpp可以查看C++的代码,目前新版本的Xcode不支持-rewrite-objc。所有资料都是之前本地的笔记目前上传到网上备份用。
NSObject的内存实现
struct NSObject_IMPL{
Class isa;
}
Class的定义
typedef struct objc_class *Class
查看内存大小
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, char * argv[]) {
NSObject *obj = [[NSObject alloc] init];
long size = class_getInstanceSize([NSObject class]);
NSLog(@"%ld",size); // 8
long mememoySize = malloc_size((__bridge const void *)(obj));
NSLog(@"%ld",mememoySize ); // 16
return 0;
}
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;
}
结构体含有一个指针isa。实际只需要8字节内存。但是实际objc源码限定了,如果对象小于16字节,直接分配16字节对象。作用是每次取内存都直接16字节对齐提升效率。
自定义对象的内存
就是继承NSObject,第一个还是isa指针,后面是普通成员变量。
struct NSObject_IMPL {
Class isa;
};
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
};
@interface Student : NSObject
{
@public
int _no;
int _age;
}
@end
24->32对齐
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface Student : NSObject
{
@public
int _no;
int _age;
int _score;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
stu->_no = 4;
stu->_age = 5;
NSLog(@"%zd", class_getInstanceSize([Student class]));// 24,实际占20,然后内存对齐后是24(指针为8,结构体内存为最大成员变量的整数倍)
NSLog(@"%zd", malloc_size((__bridge const void *)stu));// 32
}
return 0;
}
开启内存的过程
static id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone) {
// 1. 计算实例所需的内存大小(内部会调用 instanceSize -> align16 对齐)
size_t size = cls->instanceSize(extraBytes);
// 2. 在堆区开辟内存(calloc 会自动清零)
id obj = (id)calloc(1, size);
// 3. 初始化 isa 指针,将实例与类绑定
obj->initInstanceIsa(cls, hasCxxDtor);
return obj;
}
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
// 保底机制:即使对象只有 isa 指针(8字节),也强制分配 16 字节
if (size < 16) size = 16;
return size;
}
size_t fastInstanceSize(size_t extra) const {
ASSERT(hasFastInstanceSize(extra));
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// 在这里调用了 align16 进行 16 字节对齐
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
16字节对齐。理解为加15对16取余。
将传入的字节数加上 15,然后与 15 取反后的值(即二进制的 ...11110000)进行按位与(&)运算。这相当于抹去低 4 位,实现向上取 16 的倍数。
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
对象的分类
- (Class)class {
return object_getClass(self);
}
objc_getClass的本质就是获取对象的isa。isa & ISA_MASK才是真实的类对象的地址。
//
// main.m
// demo01
//
// Created by wang on 2026/9/6.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// obj就是实例对象
NSObject *obj = [[NSObject alloc] init];
// 类对象
Class objClass1 = [NSObject class];
Class objClass2 = [objClass1 class]; // 对类对象取class还是自己
Class objClass3 = objc_getClass("NSObject");
NSLog(@"%p\n%p\n%p",objClass1,objClass2,objClass3);
// 元类对象meta-class
Class objmetaClass1 = objc_getMetaClass("NSObject");
Class objmetaClass2 = object_getClass([NSObject class]);
Class objmetaClass3 = object_getClass(objmetaClass1);//对元类对象取元类对象,是自己
NSLog(@"%p\n%p\n%p\n%@",objmetaClass1,objmetaClass2,objmetaClass3,class_isMetaClass(objmetaClass2)?@"true":@"false");
}
return EXIT_SUCCESS;
}
所有对象的元类对象都指向父类的元类对象。而父类的元对象又指向NSObjecct的元对象。因此,所有对象的原来的isa都指向NSObject的元对象。
// 获取当前类的元类
Class metacls = cls->ISA();
// 核心逻辑:设置元类的 isa 指针
if (metacls->isMetaClass()) {
// 如果已经是元类,它的 isa 指向谁?
// 指向基类(Root class)的元类
metacls->initClassIsa(cls->getSuperclass()->ISA());
}
而NSObject由于没有父类,则指向自己。
// 获取当前类的父类
Class supercls = cls->getSuperclass();
// 核心边界判断
if (supercls != nil) {
// 普通子类:元类的 isa 指向【父类的元类】
metacls->initClassIsa(supercls->ISA());
} else {
// 根类 (NSObject):没有父类了,元类的 isa 指向【自己】,形成终极闭环
metacls->initClassIsa(metacls);
}
superclass
执行对象方法的时候,会优先找当前的类对象的方法列表查找,没有找到就调用父类的类对象方法列表,NSObject的superclass为空,如果NSObject还没有找到就会报错。
类方法也一样,直接找父类的类对象。区别是NSObject的superclass是NSObject的类对象。
所以,有一个有趣的事情发生了,给NSObject添加一个扩展,可以被所有的类调用类对象
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface NSObject(Hack)
- (void)unknowMethod;
@end
@implementation NSObject(Hack)
- (void)unknowMethod{
NSLog(@"NSObject unknowMethod");
NSString *className = NSStringFromClass([self class]);
// 核心判断:判断当前的 self 是不是一个类对象
if (object_isClass(self)) {
NSLog(@"👉 我是被当作【类方法 (+)】调用的!当前类名: %@, self: %@", className, self);
} else {
NSLog(@"👉 我是被当作【实例方法 (-)】调用的!当前类名: %@, self: %@", className, self);
}
}
@end
@interface Person : NSObject
@end
@implementation Person
@end
int main(int argc, const char * argv[]) {
NSObject *obj = [[NSObject alloc] init];
[obj unknowMethod];
[Person unknowMethod]; // 编译通过,可以正常使用
// [Person unknowMethod1];编译报错
return EXIT_SUCCESS;
}
// 输出
NSObject unknowMethod
👉 我是被当作【实例方法 (-)】调用的!当前类名: NSObject, self: <NSObject: 0x1007b1b90>
NSObject unknowMethod
👉 我是被当作【类方法 (+)】调用的!当前类名: Person, self: Person
Program ended with exit code: 0