理解探究,防止忘记。给出一套示例(当前运行环境64位操作系统,所以下面的计算都是基于64位操作系统的):
Person -> NSObject -> MetaClass
////////////////////////////
//Person类中无任何成员变量
Person *person = [[Person alloc] init];
//////////////////////////在C++的编译下得到:
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
}
////////////////////////////////////////
NSObject *obj = [[NSObject alloc] init];
在C++的编译下得到:
struct NSObject_IMPL {
Class isa;
}
///////////////////////////////////////
而Class为一个结构体的指针:
typedef struct objc_class *Class;
所以,要想知道OC对象的大小,就只需了解指针的大小和结构体的大小。
下面只计算NSObject的大小
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%zd",malloc_size((__bridge const void *)(obj)));
///////////////////////////输出结果为:
8
16
class_getInstanceSize
和malloc_size
产生的结果有差距
那么就需要知道这两个函数内部做了些啥了
通过查看runtime
源码知道,class_getInstanceSize
//1.
size_t class_getInstanceSize(Class cls) {
if (!cls) return 0;
// 返回对齐过的实例大小
return cls->alignedInstanceSize();
}
//2. Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
// 返回成员变量的大小
return word_align(unalignedInstanceSize());
}
由源码知道,class_getInstanceSize
返回的是成员变量的大小,而在[NSObject class]
中只有一个指向Class类型的指针。所以class_getInstanceSize([NSObject class])
获取的大小 ==等价于== 一个指向Class类型的指针的大小。
alloc的完整实现流程,可以配合着看下。下面我给出alloc
==创建实例对象==的一条实现流程:
1.
+ (id)alloc {
return _objc_rootAlloc(self);
}
2.
id _objc_rootAlloc(Class cls) {
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
3.
id class_createInstance(Class cls, size_t extraBytes) {
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
4. // Call [cls alloc] or [cls allocWithZone:nil], with appropriate shortcutting optimizations.
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false) {
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
//初始化isa
obj->initInstanceIsa(cls, dtor);
return obj;
} else {
// Has ctor or raw isa or something. Use the slower path.
//创建一个实例createInstance
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
5.
id class_createInstance(Class cls, size_t extraBytes) {
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
6.
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true,
size_t *outAllocatedSize = nil) {
...
size_t size = cls->instanceSize(extraBytes);
...
}
7.
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.至少16个字节
if (size < 16) size = 16;
return size;
}
alloc
在创建实例对象的流程中,必然会调用instanceSize
方法,所以,malloc_size
结果为16字节 。
可以看出,class_getInstanceSize()函数
是对象理论上需要的内存。alloc
是对象实际对象需要的内存。实际分配中苹果提前有一块儿一块儿的内存块儿,这些内存块儿都是16的倍数,最大是256。所以==通过alloc实际分配内存的对象,都是16的倍数,至少16==.
注意:64位和32位操作系统,字符,基本数据,指针的大小,存在差异。
image
再新建一个Person
类
//////////////////.h
#import <Foundation/Foundation.h>
@interface Person : NSObject {
int a;
}
@end
//////////////////.m
#import "Person.h"
@implementation Person
@endNSObject_IMPL
///////////////在C++的编译下得到
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int a;
};
///////////////输出结果为
16
16
class_getInstanceSize([Person class])
按照理论计算可能是12,然而得到大小为16。
1.
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
// 返回对齐过的实例大小
return cls->alignedInstanceSize();
}
2.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
3.
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
static inline size_t word_align(size_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
从源码中发现实际的占用大小经过一次内存对齐操作word_align.
通过class_getInstanceSize
获取的对象内存大小是,实际需要的内存大小,遵循结构体内存对齐的原则即可。详情可以了解结构体内存对齐.
结构体内存对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
- 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 windows(32)/VC6.0 中默认的值为8, linux(32)/GCC 中的默认值为4。
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
所以计算方式是:
1.
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;//8个字节
int a;//最后的占用大小是最大元素的倍数,所以结果为:8+4 最大元素倍数最接近的是 8 + 8
};
2.
struct NSObject_IMPL {
Class isa;//isa 指向objc_class结构体对象的指针
}
3.
typedef struct objc_class *Class;
4.
struct objc_class : objc_object {
...
}
下面,我再给出几个例子,可以分析下最后打印结果
@interface SuperPerson : Person {
int d;
}
@interface Person : NSObject {
char a[6];
}
///////////
NSLog(@"%zd",class_getInstanceSize([SuperPerson class]));
SuperPerson *p = [[SuperPerson alloc] init];
NSLog(@"%zd",malloc_size((__bridge const void *)(p)));
@interface SuperPerson : Person {
char d[3];
}
@interface Person : NSObject {
char a[6];
}
///////////
NSLog(@"%zd",class_getInstanceSize([SuperPerson class]));
SuperPerson *p = [[SuperPerson alloc] init];
NSLog(@"%zd",malloc_size((__bridge const void *)(p)));
@interface SuperPerson : Person {
int d;
}
@interface Person : NSObject {
int a;
char b;
}
///////////
NSLog(@"%zd",class_getInstanceSize([SuperPerson class]));
SuperPerson *p = [[SuperPerson alloc] init];
NSLog(@"%zd",malloc_size((__bridge const void *)(p)));
理解内存对齐
struct stu{
long a;
int b;
char c;
};
struct tea{
int n;
struct stu student;
long m;
};
//存a 对齐数为8 从对齐数的整数倍0开始存,存b 对齐数为4 从对齐数的整数倍8开始存,存c 对齐数为1 从对齐数的整数倍12开始存,遵循结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,得出16
printf("%lu\n",sizeof(struct stu));
//存n 对齐数为4 从对齐数的整数倍0开始存,存结构体stu 对齐数为8 从对齐数的整数倍8开始存,存a 对齐数为8 从对齐数的整数倍8开始存,存b 对齐数为4 从对齐数的整数倍12开始存,存c 对齐数为1 从对齐数的整数倍16开始存,存m 对齐数为8 从对齐数的整数倍24开始存,遵循结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,得出32
printf("%lu\n",sizeof(struct tea));
OC对象结构图
OC对象结构图
网友评论