iOS备忘录iOS开发笔记本将来跳槽用

iOS-Objective-C对象的本质

2018-08-08  本文已影响46人  梦蕊dream

前言:本文简述Objective-C基础知识,如有错误请留言指正。

Q:Objective-C的本质

A:Objective-C->C\C++->汇编语言->机器语言

将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

student的本质

1.NSObject是占用16字节,isa指针占用8字节,余下为空
2.Student继承自NSObject,isa指针占用8个字节,int变量占用4个字节,所以共占用 isa(8) + _no(4) + _age(4) = 16
3.可使用malloc_sizeclass_getInstanceSize验证,结果皆16个字节

Student底层实现

Q:一个Person对象(包含一个_age(int)实例变量),一个Student对象继承自Person对象(包含一个_no(int)),各占用多少内存空间

A:都占用内存大小为16

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));

输出结果:
p:24
p:32
stu:56
stu:64

Q3内存图解

其他

实时查看内存数据

Debug -> Debug Workfllow -> View Memory (Shift + Command + M)


View Memory
常用LLDB命令
memory  write  0x0000010  10
x/数量格式字节数  内存地址
x/3xw  0x10010
上一篇下一篇

猜你喜欢

热点阅读