OC对象本质是C++的结构体:因为对象涉及到不同类型,只有结构体能存储不同的结构体
- (void)test {
}
其实是系统会传递两个参数过来
// self 方法调用者
// _cmd 方法名,等价于当前方法的selector,既@selector(test)
- (void)testId:(id)self _cmd:(SEL)_cmd {
}
//转化成CPP
void test(MJPerson* self, SEL _cmd) {
}
OC对象的本质
将OC代码转换成为C\C++代码
这里最好新建项目创建两个类进行转换,因为要遵从一些上下文,否则会转换失败
切换到文件目录,使用命令行
clang -rewrite-objc xxxx.m -o xxx.cpp直接切换到iOS下64位系统可以使用的CPP代码xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc xxxx.m -o xxx.cpp
查看OC源码地址
Core Foundation硬性规定对象占用十六字节
面试题:
一个NSObject对象占用多少内存?
系统分配了
16个字节给NSObject对象(通过malloc_size函数获得)但
NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)在
64位CPU中,NSObject 对象占用大小为16字节,其中8字节为指针大小,8字节为实例对象结构体所占大小。在
32位CPU中,NSObject 对象占用大小为8字节,其中4字节为指针大小,4字节为实例对象结构体所占大小。
OC 转换为C++后,查看对象的实现,一般是类型_IMPL形式,Student转换后就是 Student_IMPL
查看class_getInstanceSize的实现(.mm 文件),在Objc 4 中
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
// NSObject Implementation
struct NSObject_IMPL {
Class isa;
};
//typedef struct objc_class *Class; // 64位占8个字节
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%zd",class_getInstanceSize([NSObject class])); // 8
NSLog(@"%zd", malloc_size((__bridge const void *)obj)); // 16
}
return 0;
}
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
// Class's ivar size rounded up to a pointer-size boundary. (返回类的成员变量的大小)
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
alloc 实际是调用 allocWithZone
--------------------- 类: NSObject.mm ---------------------
// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
--------------------- 类: Objc-runtime-new.mm ---------------------
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
--------------------- 类 objc-runtime-new.mm ---------------------
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
............
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
............
}
--------------------- 类:objc-runtime-new.h ---------------------
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.
if (size < 16) size = 16; ``
return size;
}
if (size < 16) size = 16; 主要是这句,是Core Foundation框架 硬性规定的
查看过程:
1. 将OC转换成C++代码,可以看到的NSObject的本质是一个结构体,其中包含了一个isa指针,占用了8个字节
// NSObject Implementation
struct NSObject_IMPL {
Class isa; // 8个字节
};
2. 查看源码
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
// Class's ivar size rounded up to a pointer-size boundary.
// 获取到类的实例对象的成员变量所占用的大小
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
查看alloc分配内存的过程
// 1.在源码中搜索allocWithZone,找到实现函数
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
{
id obj;
#if __OBJC2__
// allocWithZone under __OBJC2__ ignores the zone parameter
(void)zone;
obj = class_createInstance(cls, 0);
#else
if (!zone) {
obj = class_createInstance(cls, 0);
}
else {
obj = class_createInstanceFromZone(cls, 0, zone);
}
#endif
if (slowpath(!obj)) obj = callBadAllocHandler(cls);
return obj;
}
// 2. 查看class_createInstance函数
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
// 3. 查看cls->instanceSize(extraBytes);
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;
}
可以看出Core Foundation框架要求对象至少占用16个字节
计算机中的正数用源码表示,负数用补码表示,而负数的补码是其反码+1 所以出现了-128 简单的说为了避免-0,负数的算法结果是每位都加了1
iOS是小端模式,在内存中从高位向低位读取数据
内存对齐: 结构体的大小必须是最大成员大小的倍数
iOS内部操作系统:内存分配是16的倍数
instance对象(实例对象)、class对象(类对象)、meta_class对象(元类对象)
instance对象(实例对象)
-
instance对象在内存中存储的信息包括 :isa指针(也是成员变量)、其他成员变量 -
isa的地址,就是instance对象的地址,因为isa总是在首部
class对象(类对象)
每个类在内存中有且只有一个class对象
runtime 源码 在objc4 里面
isa和superClass
instance对象、class对象、meta_class对象
instance对象
每个instance对象创建出来(通过alloc、init创建),方法都只有一份,因为方法是相同的,放到类的方法列表中 (不放在对象的结构体中)
一个类的类对象(class对象)是唯一的,在内存中只会开辟一份存储空间
isa指针向顺序
class对象的superClass指针
isa和superClass总结
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
- 注意点:基类的元类的superClass指针,指向基类的类对象
isa指针细节
ISA_MASK 去源码里面获取
检测方式
实例对象
定义一个类MJPerson,创建一个instance对象(实例对象)和一个Class对象(类对象),断点打印对应的isa地址。
类对象
因为类对象的isa指针无法通过程序直接打印,可以创建一个结构体模仿类对象的实现,然后进行打印
struct mj_objc_class {
Class isa;
Class superclass;
};
// 使用
struct mj_objc_class *personClass = (__bridge struct mj_objc_class *)([MJPerson class]);
从而得出
personClass->isa & ISA_MASK:0x00000001000014c8 = meta_class
窥探struct objc_class的结构 (类对象和元类对象对应的结构体Class)
super 和 superClass的区别
- self就是给谁发消息,不一定是当前类,谁接受消息,self就代表谁
#import <Foundation/Foundation.h>
@interface NSObject (Test)
+ (void)test;
@end
//#import "NSObject+Test.h"
@implementation NSObject (Test)
//+ (void)test
//{
// NSLog(@"+[NSObject test] - %p", self);
//}
//
- (void)test
{
// self就是给谁发消息,不一定是当前类,谁接受消息,self就代表谁
NSLog(@"-[NSObject test] - %p", self);
}
@end
@interface MJPerson : NSObject
+ (void)test;
@end
@implementation MJPerson
//+ (void)test
//{
// NSLog(@"+[MJPerson test] - %p", self);
//}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"[MJPerson class] - %p", [MJPerson class]);
NSLog(@"[NSObject class] - %p", [NSObject class]);
// OC对象的调用方法,就是发送消息,发送过程中不会注意到是类方法还是对象方法
[MJPerson test];
// objc_msgSend([MJPerson class], @selector(test))
// isa -> superclass -> suerpclass -> superclass -> .... superclass == nil
[NSObject test];
// objc_msgSend([NSObject class], @selector(test))
/* 打印结果
[MJPerson class] - 0x1000011e0
[NSObject class] - 0x7fff8d775140
// self就是给谁发消息,不一定是当前类,谁接受消息,self就代表谁,所以这里的打印虽然发生在NSObject的分类中,但是其实消息是发送给MJPerson的,所以打印出的self是MJPerson的类对象
/*重点是看具体的地址*/
[NSObject test] - 0x1000011e0
[NSObject test] - 0x7fff8d775140
*/
}
return 0;
}