前言
这是一篇我记录对alloc、init分析思考的笔记。如果读者想看懂我的第二个思考,可能需要您至少了解内存的分段分页管理,如果您对其一点都不知道,可以先看这篇软文简单了解一下。另外很重要的一点是,请先思考。
思考1.对象为什么要alloc,init又是干嘛的?
很多人都知道,初始化一个对象应该这么写:
1 |
MyClass* myObj = [[MyClass alloc] init]; |
那么有没有思考过为什么呢?其实我这么写也是完全可以的:
1 2 |
MyClass *myObj = [MyClass alloc]; myObj = [myObj init]; |
我们来看看这干了啥。
alloc allocates a chunk of memory to hold the object, and returns the pointer.
就是说alloc分配了一坨 内存给对象,让它不释放,并且把地址返回给指针。
1 |
MyClass *myObj = [MyClass alloc]; |
那么这样过后myobj为什么不能被使用呢?这是因为这片内存还没有被正确的初始化。
举个栗子,万达要修房子,他们第一步一定是要先向政府搞到一块地,第二步才能在这块地上动工修楼。
这里操作系统就是政府,alloc就是去争地,init就是在地上修房子。没有调用init,房子都没有修好,别人怎么买房进去住?所以我们需要用init来初始化这片内存:
1 2 3 4 5 6 7 |
-init{ self=[super init]; // 1. if(self){ // 2. .... } return self; // 3. } |
第一步需要初始化父类的信息,比如实例变量等等。可以理解成王思聪在修房子前要询问他老爸的意见,他老爸说想娱乐会所,他没有意见的话就会修成娱乐会所,他如果有意见,就可以悄悄的在第二步里面改为修成LOL俱乐部。第三步就不说了。
最后提醒一下,不要这样写:
1 2 |
MyClass* myObj = [MyClass alloc]; myObj=[myObj init]; |
因为你可能会忘记在第二行加init,并且代码也会增长。
思考2.关于alloc的思考
在思考1中我们说了:alloc分配了一坨 内存给对象,让它不释放,并且把地址返回给指针。这里主要有两个问题:
- 调用alloc后内存是直接映射到堆还是只分配给了虚拟内存?
- 这一坨内存到底是多大?
我们依次来展开。
可能有些读者不明白第一个问题是什么意思,这里需要额外讲一些关于内存的东西,其实这是iOS开发很重要的东西,不管是面试还是学习都可能会用到。
额外的东西
iOS里的内存是有分类的,它分成Clean Memory和Dirty Memory。顾名思义,Clean Memory是可以被操作系统回收的,Dirty Memory是不可被操作系统回收的。
- Clean Memory:在闪存中有备份,能再次读取重建。如:
- Code(代码段),framework,memory-mapped files
- Dirty Memory:所有非Clean Memory,如:
- 被分配了的堆空间,image cache
举个栗子,在这样的代码中:
1 2 3 4 5 6 7 8 9 10 11 |
- (void)dirtyOrCleanMemory { NSString *str1 = [NSString stringWithString:@"Welcome!"]; // 1. NSString *str2 = @"Welcome"; // 2. char *buf = malloc(100 * 1024 * 1024); // 3.分配100M内存给buf for (int i = 0; i < 3 * 1024 * 1024; ++i) { buf[i] = rand(); } // 4.buf的前3M内存被赋值 } |
对每行分析:
1.Dirty Memory。
因为stringWithString:是在堆上分配内存的,如果我们不回收它的话,系统会一直占用这块内存。
2.Clean Memory。
因为用这样的方法创建的是一个常量字符串,常量字符串是放在只读数据段的,如果这块内存被释放了,而我们又访问它的时候,操作系统可以在只读数据段中把值再读取出来重建这块内存。(ps:所以用这种方法创建的string是没有引用计数的。)
接下来的知识就是引出思考问题1、2比较重要的点了:
3.Clean Memory。
这个时候buf指向的100M内存区域是Clean Memory的,因为操作系统是很懒的,只有当我们要用到这块区域的时候才会映射到物理内存,没有使用的时候只会分配一块虚拟内存给buf。读起来很绕口,上张图:
可以看到虚拟内存和物理内存没有映射关系,所以是Clean Memory的。
4.Dirty & Clean Memory混合。
前3M是Dirty Memory,后97M是Clean Memory。这句for语句执行完成后,buf的前3M内存被赋值,也就是buf的前3M被使用了,所以这个时候的映射关系是这样的:
额外的东西Done.
回到主线
- 调用alloc后内存是直接映射到堆(物理内存)还是只分配给了虚拟内存?
- 一坨内存的一坨是多大?
这个时候我们的第一个问题读者应该能明白了。那么我们怎么验证alloc是直接映射到堆上还是只分配给虚拟内存呢?这个问题让我想了好些天,最后xo哥想到了一剂良药来验证,那就是用instrument来推反。
使用instrument来证反
我们假设的论点是:对象收到alloc消息后只在虚拟内存分配空间。
这里需要一丁点代码。
1.我们随便新建个工程。
2.然后做个model类:
1 2 3 4 5 6 7 8 9 |
#import <Foundation/Foundation.h> @interface XOModel : NSObject { int a1; NSString *a2; } @end |
3.在controller里给view加一个点击事件:s="crayon-line" id="crayon-5812cf868dfcc917804147-7">}