提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
提示:这里可以添加本文要记录的大概内容:
提示:以下是本篇文章正文内容,下面案例可供参考
内存分为五个区。
栈区(系统管理的地方)
堆区(程序员控制的地方)
常量区(全局去)
静态区和代码区
由编译器自动分配并释放,存放函数的参数值,基本类型的变量和对象引用类型,方法调用的实参也是保存在栈区。
可以把栈区看作一个临时寄存,交换的内存区
栈是系统数据结构,对应的线程/进程是唯一的
优点是快速高效,缺点是有限制,数据操作不灵活
栈区是一段连续的内存区域,由高地址向低地址存储,遵循先进后出(FILO)原则,一般在运行时进行分配,内存空间由系统管理,变量过了作用域范围后就会自动释放。参数,函数,局部变量都存符在栈区。参数入栈是从前往后入栈,而结构体是从后往前入栈。
由程序员分配和释放,构造的对象和数组如果不释放,可能造成内存泄漏,程序结束时可能会由系统回收。
比如通过new,alloc,malloc,realloc分配的内存块就存符在堆区
堆向高地址扩展的数据结构,是不连续的内存区域。堆中的所有东西都是匿名的,这样不能按名字访问,只能通过指针访问。
对于堆来讲,频繁的new/delete会造成内存空间的不连续性,使程序效率降低
堆向高地址扩展的数据结构,是不连续的内存区域。程序员负责在何时释放内存,在ARC中,计数器为0的时候,在当次的runLoop结束后,释放掉内存。堆区中的所有东西都是匿名的,这样奴能按照名字放完,只能通过指针访问。
文字常量区:该区是编译时分配的内存空间,在程序运行过程中,此内存中的数据一直存在。程序结束后由系统释放。
存放常量:整形,字符型,浮点型,字符串
全局区(静态区)(static):全局变量和静态全局变量的存储是放在一起的,初始化的全局变量和静态全局变量存放在一块区域,为初始化的全局变量和静态变量在相邻的另一块地址,程序结束后由系统释放。
全局区又可分为为初始化全局区(BSS段)和初始化全局区(DATA段)
程序代码区:用来存放函数的二进制代码
代码段需要防止在运行时被非法修改,所以只允许读取操作,不允许写入操作
如果初始化方法以new,alloc,retain,copy开头都是在堆区,被引用计数管理的方法也是在堆区
常量会在栈区
如果是在方法执行的过程中,定义在本地的原生类型(或者值类型)。那么它肯定是在栈上,上函数执行结束时直接销毁,而其他的引用类型(或者oc中的interface)都是在堆上创建的,由ARC负责清理
target->Build Settings
空指针指没有指向存储空间的指针(存的是nil)
给空指针发消息是没有反应的
只要一个对象被释放了,我们称这个对象为【僵尸对象(不能在使用的对象)】
当一个指针指向一个僵尸对象(不能在使用的对象),我们就称这个指针为【野指针】
只要给一个野指针发送消息就会报错
**但是我在这样运行时并没有报错,按道理来说在第二次调用release函数时它才会报错,但是它此时并没有报错
但是我们在使用它一次之后,就出现了应该出现的问题
可以理解为release一次之后,编译器意识不到这个对象引用计数已经变为0了,我们再次使用这个对象时,编译器才能意识到它变为了0 ,然后报错。
NSObject *p = [[NSObject alloc] init]; // 执行完引用计数为 1。
[p release]; // 执行完引用计数为 0,实例对象被释放。
p = nil; // 此时,p 变为了空指针。
[p release]; // 再给空指针 p 发送消息就不会报错了。
[p release];
多个对象是通过setter方法产生联系的。内存管理方法也是在setter方法中,dealloc方法中实现的。
#import <Foundation/Foundation.h>
#import "Room.h"
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person* p = [[Person alloc] init];
Room* r = [[Room alloc] init];
r.no = 888;
[r release];
[p release];
}
return 0;
}
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Room : NSObject
@property int no;
@end
NS_ASSUME_NONNULL_END
//
#import "Room.h"
@implementation Room
@end
#import <Foundation/Foundation.h>
#import "Room.h"
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject {
Room* room;
}
- (void)setRoom : (Room*) room;
- (Room*)room;
@end
NS_ASSUME_NONNULL_END
#import "Person.h"
@implementation Person
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person* p = [[Person alloc] init];
Room* r = [[Room alloc] init];
r.no = 888;
p.room = r;
[r release];
NSLog(@"------");
[p release];
}
return 0;
}
在成员变量前加上@property,系统就会自动帮我门生成基本的setter/getter方法,但是不会生成内存管理相关代码
@property (nonatomic) int val;
如果在@property后价上assign,系统也不会帮我们生成setter方法内存管理的代码,仅仅生成普通的getter/setter方法,默认什么都不写就是assign
@property (nonatomic, aassign) int val;
如果在property后比加上retain,系统就会帮我们生成getter/setter方法内存管理的代码,但是仍然需要我们自己重写delloc方法
@property (nonatomic, retain) Room* room;
当我们不在使用一个对象的时候应该将其空间释放,为了解决如何释放空间的问题,Objective-C提供了autorelease方法
Person* p = [Person new];
p = [p autorelease];
NSLog(@"%lu", [p retainCount]);
autorelease实际上只是把对releae的调用延迟了,对于每一个autoreleasepool中,该pool被释放时,pool中的所有对象都会被调用release方法
NSAutoreleasePool* autoreleasePool = [[NSAutoreleasePool alloc] init];
Person* p = [[[Person alloc] init] autorelease];
[autoreleasePool drain];
不是说放到自动释放池中的代码都会加入到自动释放池
@autoreleasepool {
// 因为没有调用 autorelease 方法,所以对象没有加入到自动释放池
Person *p = [[Person alloc] init];
[p run];
}
在自动释放池的外部发送autorelease不会被加入到自动释放池中
autorelease是一个方法,只有在自动自动释放池中调用才有效
@autoreleasepool {
}
// 没有与之对应的自动释放池, 只有在自动释放池中调用autorelease才会放到释放池
Person *p = [[[Person alloc] init] autorelease];
[p run];
// 正确写法
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
}
// 正确写法
Person *p = [[Person alloc] init];
@autoreleasepool {
[p autorelease];
}
自动释放池是以栈的形式存在
由于栈只有一个入口,所以调用autorelease会将对象放到栈顶的自动释放池
栈顶就是离调用autorelease方法最近的自动释放池
@autoreleasepool { // 栈底自动释放池
@autoreleasepool {
@autoreleasepool { // 栈顶自动释放池
Person *p = [[[Person alloc] init] autorelease];
}
Person *p = [[[Person alloc] init] autorelease];
}
}
自动释放池中不应该放占用内存较大的对象
尽量避免对大内存使用该方法,对于这种延迟释放机制,尽量少用
不要把大量循环操作放到同一个@autoreleasepool之内,会造成内存峰值上升
// 内存暴涨
@autoreleasepool {
for (int i = 0; i < 99999; ++i) {
Person *p = [[[Person alloc] init] autorelease];
}
}
// 内存不会暴涨
for (int i = 0; i < 99999; ++i) {
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
}
}
@autoreleasepool {
// 错误写法, 过度释放
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
@autoreleasepool {
Person *p = [[[Person alloc] init] autorelease];
[p release]; // 错误写法, 过度释放
}