美文网首页
OC对象的本质(二)

OC对象的本质(二)

作者: dandelionYD | 来源:发表于2018-12-21 14:17 被阅读0次

(本文所运行的Demo是在集成了objc4源码基础上的,详见:gitHub

本文所写的项目详见:OCBasicDemo

从前面的《OC对象的本质(一)》我们了解了为一个NSObject对象系统到底分配了多少的内存
接下来我们再来分析下:自定义对象 内存,底层到底是怎么实现的.

我们自定义一个Student类如下:

#import <Foundation/Foundation.h>
@interface Student : NSObject{
    @public
    int age;
    int number;
}
@end

@implementation Student
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Start");
        Student *stu = [[Student alloc]init];
        stu->age = 18;
        stu->number = 1;
        NSLog(@"End");
    }
    return 0;
}

使用终端:进入main.m 我们将它转为C/C++代码:

xcrun -sdk iphoneos clang  -arch arm64 -rewrite-objc main.m -o myMain.cpp

我们在myMain.cpp里面发现

struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int age;
    int number;
};

NSObject_IMPL为:
struct NSObject_IMPL {
    Class isa;
};

下面我来用代码来验证下:

#import <Foundation/Foundation.h>
@interface Student : NSObject{
    @public
    int age;
    int number;
}
@end

@implementation Student
@end

struct Student_IMPL {
    Class isa;
    int age;
    int number;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Start");
        Student *stu = [[Student alloc]init];
        stu->age = 18;
        stu->number = 1;
        
        //我们强转下
        struct Student_IMPL *stu2 = (__bridge struct Student_IMPL *)(stu);
        NSLog(@"%d<--->%d",stu2->age,stu2->number);//18 1
        
        NSLog(@"%@",stu);//0x100f22bb0
        NSLog(@"%p",&(stu2->isa));//0x100f22bb0
        NSLog(@"%p",&(stu->age));//0x100f22bb8
        NSLog(@"%p",&(stu2->age));//0x100f22bb8
        NSLog(@"%zd", class_getInstanceSize([Student class])); //16个字节
        NSLog(@"%zd", malloc_size((__bridge const void *)stu)); //16个字节
        NSLog(@"End");
    }
    return 0;
}

分析:
打印stu地址和stu2(stu2->isa)的地址是一样的
打印stu->age和stu2->age地址是一样的


我们再使用viewMemory工具来看


viewMemory_OC4.png

我们也可以看出:一个isa所占8个字节,2个int各占4和字节

stu对象内存分配图_OC5.png

结论:

Student对象
@interface Student : NSObject{
    @public
    int age;
    int number;
}
@end

本质:
struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int age;
    int number;
};

其中NSObject_IMPL为:
struct NSObject_IMPL {
    Class isa;
};

思考题:
以下person和student实例对象各占多少字节?

#import <Foundation/Foundation.h>
@interface Person : NSObject{
    @public
    int age;
}
@end
@implementation Person
@end

@interface Student : Person{
    @public
    int score;
}
@end
@implementation Student
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student *stu = [[Student alloc]init];
        stu->age = 1;
        stu->score = 2;
        NSLog(@"%p,%p",&(stu->age),&(stu->score));
        NSLog(@"End");
    }
    return 0;
}

通过转化为C\C++代码:

struct Student_IMPL {
    struct Person_IMPL Person_IVARS;//16个字节
    int score;//4个字节
};//16个字节(因为Person_IMPL正好空了4个字节出来)

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;//8个字节
    int age;//4个字节
};//16个字节(内存对齐:结构体的大小必须是最大成员大小的倍数)

struct NSObject_IMPL {
    Class isa;//8个字节
};

//通过viewMemory我们也可以看出来个数为16


viewMemory_OC6.png

下面我们改变下 我们修改score类型为double来看看

@interface Person : NSObject{
    @public
    int age;
}
@end
@implementation Person
@end

@interface Student : Person{
    @public
    double score;
}
@end
@implementation Student
@end

转化为C\C++代码
struct Student_IMPL {
    struct Person_IMPL Person_IVARS;//16字节
    double score;//8个字节
};//32个字节(内存对齐:结构体的大小必须是最大成员大小的倍数)

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;//8个字节
    int age;//4个字节
};//16个字节

struct NSObject_IMPL {
    Class isa;//8个字节
};

    Student *stu = [[Student alloc]init];
    stu->age = 1;
    stu->score = 2;
    NSLog(@"%p,%p",&(stu->age),&(stu->score));
    NSLog(@"%zd", malloc_size((__bridge const void *)stu));//32
    NSLog(@"End");

使用viewMemory来验证


viewMemory_OC7.png

内存图图见下:

viewMemory_OC8.png

我们再来个栗子:(来看看内存的具体分布)

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

@interface Person : NSObject{
    int _age;
    int _height;
    int _no;
}
@end
@implementation Person
@end

struct Person_IMPL{
    Class isa;
    int _age;
    int _height;
    int _no;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        NSLog(@"%zd", sizeof(struct Person_IMPL)); // 24
        NSLog(@"%zd", class_getInstanceSize([Person class]));//24
        NSLog(@"%zd", malloc_size((__bridge const void *)(p)));//32
    }
    return 0;
}

使用终端转为C\C++:

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;//8个字节
    int _age;//4个字节
    int _height;//4个字节
    int _no;//4个字节
};//24字节(结构体内存对齐)

struct NSObject_IMPL {
    Class isa;
};

分析:我们在 Person *p = [[Person alloc] init];处打一个断点,然后单步走和断点的方式

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;
  }
  发现此时的返回的size的大小就是24个字节
  但为啥 是32个字节呢??(猜测 估计是malloc方法里面做了处理)

通过分析malloc源码
或者libmalloc源码编译,自己可以去试着打个断点去分析下
在nano_zone_common.h里面
NANO_MAX_SIZE: Buckets sized {16, 32, 48, ..., 256}
真正在底层ios(苹果操作系统)分配的时候还会对齐一下(以方便自己访问最快),有空间桶这个概念(16的倍数给的)

我们在xcode里面在来验证下:

#import <Foundation/Foundation.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        char *m;
        m = (char *)(malloc(24)); //动态分配24个字节
        NSLog(@"所占大小%lu",malloc_size(m)); //32
    }
    return 0;
}
发现打印出来的:实际上系统分配了32个字节

补充:
class_getInstanceSize:创建一个实例对象,至少需要多少内存
malloc_size:创建一个实例对象,系统实际上分配了多少内存

sizeof:是一个运算符:在程序编译的时候就知道值了 如(sizeof (p)) Persion *p = [Persion alloc]init];
而:class_getInstanceSize 是在运行时才知道的

友情链接:

相关文章

网友评论

      本文标题:OC对象的本质(二)

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