前言:本文简述Objective-C基础知识,如有错误请留言指正。
Q:Objective-C的本质
A:Objective-C->C\C++->汇编语言->机器语言
- Objective-C的面向对象都是基于C\C++的数据结构实现的
- Objective-C的对象、类主要是基于C\C++的
结构体
数据结构实现的 - Objective-C的对象内存至少是16字节
- 内存对齐:结构体的大小必须是最大成员变量大小的倍数
将Objective-C代码转换为C\C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc
OC源文件 -o
输出的CPP文件
如果需要链接其他框架,使用-framework参数。比如-framework UIKit
Q:一个OC对象在内存中如何布局的
例:NSObject
NSObject *obj = [[NSObject alloc] init];
底层实现
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
//Class:isa指针
//64位下 占8个字节;32位下占4个字节
typedef struct objc_class *Class;
结构体只有isa成员变量,isa地址就是结构体的地址。
NSObject底层实现
Q:一个NSObject对象占用多少内存?
A:系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)
#import <objc/runtime.h>
#import <malloc/malloc.h>
NSObject *obj = [[NSObject alloc] init];
//获取NSObject实例对象的成员变量所占用的大小 : 8
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
//获取*obj指针所指向内存的大小:16
NSLog(@"%zd",malloc_size((__bridge const void *)obj));
Q:例如一个student继承自NSObject,内部包含两个int变量,那么student占用多少内存
A:占用内存大小 16
- 子类的成员变量:父类成员变量+子类成员变量
- isa指针地址就是该结构体的内存地址
1.NSObject是占用16字节,isa指针占用8字节,余下为空
2.Student继承自NSObject,isa指针占用8个字节,int变量占用4个字节,所以共占用 isa(8) + _no(4) + _age(4) = 16
3.可使用malloc_size
和class_getInstanceSize
验证,结果皆16个字节
Q:一个Person对象(包含一个_age(int)实例变量),一个Student对象继承自Person对象(包含一个_no(int)),各占用多少内存空间
A:都占用内存大小为16
- Person对象占用:16;
isa(8) + _age(4) < 16
- Student对象占用:16;
isa(8) + _age(4) + _no(4) = 16
Person、Student本质
Q1:一个Student对象继承自NSObject对象(包含_no(int)、_age(int)),占用多少内存空间
A1:分配空间为16
@interface Student : NSObject
{
int _age;
int _no;
}
@end
@implementation Student
@end
Student *stu = [[Student alloc] init];
NSLog(@"stu:%zd",class_getInstanceSize([Student class]));
NSLog(@"stu:%zd",malloc_size((__bridge const void *)stu));
输出结果:stu:16 stu:16
不做多余解释
Q2:一个Student对象继承自NSObject对象(包含_no(int)、_age(int)、_height(int)),占用多少内存空间
A2:分配内存32
@interface Student : NSObject
{
int _age;
int _no;
int _height;
}
@end
Student *stu = [[Student alloc] init];
NSLog(@"stu:%zd",class_getInstanceSize([Student class]));
NSLog(@"stu:%zd",malloc_size((__bridge const void *)stu));
输出结果:stu:24 stu:32
解答:
所需内存解释如下:
转化C/C++
struct Student_IMPL{
struct NSObject_IMPL NSOBJECT_IVARS;//8byte
int _age;//4byte
int _no;//4byte
int _height;//4byte
}
//总共需要8+4+4+4=20;内存对齐,isa指针8字节,需要内存为24字节,结构体需要24个字节
分配内存解释如下:
在Runtime的allocWithZone
层层追踪下,最终分配内存地址指向void *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
runtime 追踪源码
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;
}
#define --------------------------------------------------
id
class_createInstance(Class cls, size_t extraBytes)
{
return _class_createInstanceFromZone(cls, extraBytes, nil);
}
#define --------------------------------------------------
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;
}
#define --------------------------------------------------
void *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
#define ------------------------分配内存--------------------------
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;
}
继续追踪calloc,找到库libmalloc
下载地址:https://opensource.apple.com/tarballs/
void *
calloc(size_t num_items, size_t size)
{
void *retval;
retval = malloc_zone_calloc(default_zone, num_items, size);
if (retval == NULL) {
errno = ENOMEM;
}
return retval;
}
#define -------------------------------------------------
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;
}
综上:源码追踪难读懂,总结为:
结构体计算内存大小,存在内存对齐,按照isa(8byte)进行对齐
操作系统分配内存时候也存在内存对齐,iOS 在堆空间分配内存都是16的倍数
class_getInstanceSize([Student class]):至少需要24字节
malloc_size((__bridge const void *)stu):实际分配32字节
Q3:Person继承自NSObject(内含3个int成员变量);Student继承自Person(内含8个int成员变量),问各实例变量分配内存多少
A3:Person分配32字节;Student分配64字节
@interface Person : NSObject
{
int _age;
int _name;
int _bro;
}
@end
@implementation Person
@end
@interface Student : Person
{
int _dog;
int _cat;
int _book;
int _phone;
int _bicycle;
int _hat;
int _clothes;
int _pants;
}
@end
@implementation Student
@end
Person *p = [[Person alloc] init];
NSLog(@"p:%zd",class_getInstanceSize([Person class]));
NSLog(@"p:%zd",malloc_size((__bridge const void *)p));
Student *stu = [[Student alloc] init];
NSLog(@"stu:%zd",class_getInstanceSize([Student class]));
NSLog(@"stu:%zd",malloc_size((__bridge const void *)stu));
Q3内存图解输出结果:
p:24
p:32
stu:56
stu:64
其他
实时查看内存数据
Debug -> Debug Workfllow -> View Memory (Shift + Command + M)
View Memory
常用LLDB命令
- 打印:print、p
- 打印对象:po
- 格式:x是16进制,f是浮点,d是10进制
- 字节大小:
b:byte 1字节,h:half word 2字节
w:word 4字节,g:giant word 8字节 - 修改内存中的值:memory write 内存地址 数值
memory write 0x0000010 10
- 读取内存:memory read/数量格式字节数 内存地址
x/数量格式字节数 内存地址
x/3xw 0x10010
网友评论