美文网首页iOS面试总结
iOS底层原理--OC对象的本质

iOS底层原理--OC对象的本质

作者: 谦谦君子修罗刀 | 来源:发表于2019-03-21 17:56 被阅读58次
    算春长不老,人愁春老,愁只是,人间有.png
    1、NSObject的本质是什么?

    分析: OC代码的底层实现实质是C/C++代码,继而编译成汇编代码,最终变成机器语言。
    打开终端,进入main.m所在的文件夹,通过clang rewirte-objc main.m -o main.cppxcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
    代码,生成cpp文件。
    在cpp文件中找到如下代码:

    struct NSObject_IMPL {
        Class isa;
    }
    

    NSObject的底层实现实质是一个结构体。而结构体中的成员isa是Class类型,通过源码typedef struct objc_class *Class可知它是一个指针。在64为环境下指针占8个字节,而在32位机下是占4个字节。因此该结构体占8个字节(因为该结构体只有一个成员)。

    2、NSObject对象占多少内存?

    分析: 通过打印

    NSObject *obj = [[NSObject alloc]init];
    NSLog(@"%zd",malloc_size((__bridge const void *)obj));
    

    可知NSObject对象占16个字节。
    那么与题1所述结构体中占8个字节是否冲突?
    通过打印NSLog(@"%zd",class_getInstanceSize([NSObject class]))获取NSObject类的实例对象的成员变量所占用的(内存对齐之后)大小,显示确实为8个字节。
    在objc的源码中找到class_getInstanceSize方法,发现它返回的是cls->alignedInstanceSize(),对它的描述为Class's ivar size rounded up to a pointer-size boundary意指返回成员变量占据的大小。因此创建一个NSObject对象需要分配16个字节,只是真正利用的只有8个字节,即isa这个成员的大小。
    事实上,查看allocWithZone的源码发现它最底层的创建实例的方法实际上是调用了C语言的calloc方法,在该方法中,规定若分配的字节不满16将把它分配为16个字节。

    3、若一个Student类继承自NSObject类,那么Student类的对象占多少内存?

    分析: 新建Student类,添加成员变量。通过clang反编译,打开cpp文件找到Student类的底层实现。

    struct Student IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        // 还有Student类的成员变量
        //……
    }
    

    从这段代码可以看出,若一个类继承自另一个类,则它的底层会将父类的成员变量放在结构体的最前面,此后依次放置本类的成员变量。而从之前的分析可知,NSObject_IMPL的本质就是一个装有成员变量isa的结构体,因此,Student类对象所占的内存为isa的内存8加上Student类成员变量所占的空间。若不满16个字节,会强制分配到16个字节。另,由于<u>内存对齐</u>的规定,结构体的最终大小必须是最大成员的倍数。

    #import <malloc/malloc.h>
    @interface Student:NSObject{
       @public
        int age;
        int no;
        int address;
        NSString *name;
        NSString *name2;
        
    }
    @end
    
    @implementation Student
    
    @end
    
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            Student *stu = [[Student alloc]init];
            NSLog(@"%zd",malloc_size((__bridge const void *)stu));
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    

    打印结果如下图所示:


    结果

    但是如果改成如下列代码这样,打印结果却是32

    #import <malloc/malloc.h>
    @interface Student:NSObject{
       @public
        int age;  // 4
        int no;   // 4
        int address;   // 4
    }
    @end
    
    @implementation Student
    
    @end
    
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            Student *stu = [[Student alloc]init];
            NSLog(@"%zd",malloc_size((__bridge const void *)stu));
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    

    student类的三个成员变量占用内存12,加上父类结构体占用8个字节,共20个。由于内存对齐,取8的倍数为24个。但是通过查询源码可知,分配内存之时,最后调用的是文件stdlib.h的calloc方法。该方法的具体实现如下:

    calloc(size_t num_items,size_t size)
    {
        void *retval;
        retval = malloc_zone_calloc(defalt_zone,num_items,size);
        if (retval == NULL) {
            errno = ENOMEM;
        }
        return retval;
    }
    

    第一个参数代表的是分配几块内存区域,第二个参数代表这块区域的大小该函数会调用malloc_zone_calloc函数。这个函数有一个Buckets size的概念。在iOS堆空间分配内存时,分配的内存都是16的倍数。
    附:苹果官方开放源码

    相关文章

      网友评论

        本文标题:iOS底层原理--OC对象的本质

        本文链接:https://www.haomeiwen.com/subject/aqmcvqtx.html