看了一些资料,对oc更加深入了解,记录一下。
一、得到对象占用内存
直接上代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
NSLog(@"%zd",malloc_size((__bridge const void *)obj));
}
return 0;
}
2018-08-03 16:49:14.945686+0800 NSObject本质[6765:234469] 8
2018-08-03 16:49:14.945962+0800 NSObject本质[6765:234469] 16
Program ended with exit code: 0
此时可能会有疑问,都是获取对象大小的方法,为什么不一样呢。
一起来看看源码:地址
1.https://opensource.apple.com/tarballs/
2.找到objc4
3.找到最新版本(数字最大的那个)
4.搜索class_getInstanceSize
源码如下:
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
看注释说,返回成员变量的大小
所以说malloc_size
是实际占用的内存大小
二、为什么是16字节?
我们再来看源码,搜索+ (id)allocWithZone:(struct _NSZone *)zone
// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
//_objc_rootAllocWithZone
//class_createInstance
//_class_createInstanceFromZone
//instanceSize
最终找到:
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
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;
}
alignedInstanceSize
由一可知,这个方法返回8字节(class_getInstanceSize
)小于16字节,也就是说,创建一个NSObject 对象,最小占用内存为16字节
三、用C++重写OC 代码,看看NSObject对象本质
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
找到NSObject 实现
struct NSObject_IMPL {
Class isa;
};
是一个结构体,结构体只有一个成员,就是isa
。
isa
是一个typedef struct objc_class *Class;
类型的指针,我们都知道指针占用8个字节。
我们看看NSObject 对象的内存编码:
前8个字节是isa指针地址,后面的字节都是00。所以后面8个字节是没有用到的
三、用C++重写OC 代码,看看自定义Student对象本质
@interface Student:NSObject
{
@public
int _no;
int _age;
}
@end
@implementation Student
@end
重写之后找到代码如下:
struct NSObject_IMPL {//8个字节 【NSObject alloc】 16个字节
Class isa;
};
struct Student_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _no;
int _age;
};
对于继承于NSObject的对象, 会包含一个父类NSObject的isa指针和两个成员变量。我们知道由于struct NSObject_IMPL NSObject_IVARS
占用8个字节。
(wait...不是16字节吗?[NSObject alloc] 16(8+4+4)个字节
,isa是8个字节
,别搞混,搞混了回去再看一遍)
所以对于一个Student
对象,他占用的字节数是:16字节,我们来验证一下
Student *stu = [[Student alloc] init];
stu->_age = 4;
stu->_no = 5;
NSLog(@"%zd",malloc_size((__bridge const void *)stu));
2018-08-03 17:26:52.725832+0800 NSObject本质[7110:255889] 16
前8字节是isa 指针的地址,后面的05 00 00 00 和 04 00 00 00,由于是小端读取,所以是高地址读取 00 00 00 05 和 00 00 00 04
通过db 命令,我们可以改内存里的值,如图:
x
:读取内存memory write 地址 数据
:更改成员变量的值
总结:
1.8字节 isa 指针
+ 子类和父类(继承链上)的成员变量
== 对象内存大小
。(内存对齐后)
2.instance对象内存最小为16字节。
3.instance对象内存大小是16字节的最小整数倍。
2.alloc 出来的instance对象 ,只有成员变量,没有方法。方法放在类对象中(因为只需要一份)。
**注释:
由于iOS系统会内存对齐,所以,创建出来的对象是16的整数倍
#define NANO_MAX_SIZE 256 /* Buckets sized {16, 32, 48, 64, 80, 96, 112, ...} */
calloc
函数做了iOS
系统级别的内存对齐
void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
void *ptr;
size_t alloc_size;
if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
internal_check();
}
if (os_mul_overflow(num_items, size, &alloc_size) || alloc_size > MALLOC_ABSOLUTE_MAX_SIZE){
errno = ENOMEM;
return NULL;
}
ptr = zone->calloc(zone, num_items, size);
if (malloc_logger) {
malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
(uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
}
return ptr;
}
补充:
对于属性:
@interface Person : NSObject
@property (nonatomic,assign) int age;
@property (nonatomic,assign) int age2;
@property (nonatomic,assign) int age3;
@property (nonatomic,assign) int age4;
@property (nonatomic,assign) int age5;
@property (nonatomic,assign) int age6;
@property (nonatomic,assign) int age7;
@end
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
};
// @property (nonatomic,assign) int age;
// @property (nonatomic,assign) int age2;
// @property (nonatomic,assign) int age3;
// @property (nonatomic,assign) int age4;
// @property (nonatomic,assign) int age5;
// @property (nonatomic,assign) int age6;
// @property (nonatomic,assign) int age7;
/* @end */
出现这种情况,其实c++ 重写 只是参考,本质还是
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
.
.
.
};
// @property (nonatomic,assign) int age;
// @property (nonatomic,assign) int age2;
// @property (nonatomic,assign) int age3;
// @property (nonatomic,assign) int age4;
// @property (nonatomic,assign) int age5;
// @property (nonatomic,assign) int age6;
// @property (nonatomic,assign) int age7;
/* @end */