iOS 两个容易混淆的函数:class_getInstanceS

2023-04-08  本文已影响0人  笔头还没烂
  1. iOS 两个容易混淆的函数:class_getInstanceSize & malloc

    • 示例代码如下:

      #import <Foundation/Foundation.h>
      #import <objc/runtime.h>
      #import <malloc/malloc.h>
      
      struct NSObject_IMPL
      {
          Class isa;
      };
      
      struct Person_IMPL{
          struct NSObject_IMPL NSOBJECT_IVARS;
          int _age;
          int _height;
          int _no;
      };
      
      @interface Person : NSObject
      {
          //年龄
          int _age;
          //身高
          int _height;
          //学号
          int _no;
      }
      @end
      
      @implementation Person
      
      @end
      
      int main(int argc, char * argv[]) {
      
          @autoreleasepool {
              // Setup code that might create autoreleased objects goes here.
              NSObject *obj = [[Person alloc] init];
              /**
              sizeof 是针对传入的类型作计算
              sizeof 是运算符,不是函数,编绎之后就能立马确定结果。编绎后根据传入的类型能算出结果,随后类似宏定义编绎后替换源代码一样,将结果与 sizeof 表达式进行替换。
               */
              NSLog(@"%zd",sizeof(struct Person_IMPL));
              size_t size1 = class_getInstanceSize([Person class]);
              size_t size2 =  malloc_size((__bridge const void *)(obj));
              NSLog(@"%zd",size1);
              NSLog(@"%zd",size2);
          }
          return 0;
      }
      

      输出结果:

      24
      24
      32

      查看苹果的相关源码(源码地址:https://github.com/apple-oss-distributions/libmalloc/tags),相关源码如下:

      1. 在 malloc.c 文件中搜索 “calloc(size” 可得:

        void *
        calloc(size_t num_items, size_t size)
        {
            return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
        }
        
      2. 跳转 _malloc_zone_calloc 方法,可得:

        MALLOC_NOINLINE
        static void *
        _malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,malloc_zone_options_t mzo)
        {
            if (zone == default_zone && !lite_zone) {
                // Eagerly resolve the virtual default zone to make the zone version
                // check accurate
                zone = malloc_zones[0];
            }
        
            if (os_unlikely(malloc_instrumented || malloc_check_start ||
                        malloc_logger || zone->version < 13)) {
                return _malloc_zone_calloc_instrumented_or_legacy(zone, num_items,     size, mzo);
            }
        
            // zone versions >= 13 set errno on failure so we can tail-call
            return zone->calloc(zone, num_items, size);
        }
        
      3. /* Buckets sized {16, 32, 48, ..., 256} */

      小结:

      1. class_getInstanceSize 返回的实际上就是结构体所有成员变量的大小,通过上面代码的测试,它的作用与 sizeof 计算一个结构体所有成员大小的结果是一致的。

      2. malloc 是通过 calloc(1,size) 方法最终算出需要给对象分配多大的内存空间。此处传入的 size 通过源码也能发现,实际上就是等于 class_getInstanceSize 返回的大小。而他们最终分配的内存空间大小差异就在于:malloc 还多了 calloc 方法这一层的处理。

      3. malloc 是在堆内存中进行操作,堆内存的内存分配也是遵循了内存对齐的原则。这里跟 bucketSize 的大小有关,即分配的内存大小必须是16的倍数。因此,传入的 size 是24,经过 calloc 这一层处理之后,返回的就是 32.

  2. 结论:

    1. 创建一个实例对象,至少需要多少内存?

      #import <objc/runtime.h>
      class_getInstanceSize([NSObject class]);
      
    2. 创建一个实例对象,实际上分配了多少内存?

      #import <malloc/malloc.h>
      malloc_size((__bridge const void *)obj);
      
上一篇下一篇

猜你喜欢

热点阅读