这里面我着重强调一下,在OC中是没有没空指针异常的。这跟java不一样,如果一个空对象调用方法,则什么都不会发生。
首先我们来看看内存分布
栈:存放的是局部变量,这块存储区域是系统管理的,不需要我们管理。
堆:存放的是OC中的对象,这块存储区域是程序员自己管理的。它是动态存储区域。
常量区:存放的是常量,这块存储区域是系统管理的。
全局区:存放的时全局变量和静态变量,这块存储区域是系统管理的。
代码区:存放分是代码,这块区域是系统管理的。
指针存放在栈中,而指针指向的变量是存放在堆中。因此当指针被回收的时候,它的对象并不会被回收。
MRC(Manual Reference Counting)手动内存管理|手动引用计数
在OC的内存管理中:引进了引用计数。
1、当一个对象被建立的时候,它的引用计数是1。
2、当一个对象的引用计数是0的时候,这个对象将会被回收。
引用计数
1、release 引用计数-1
2、retain 引用计数+1 (这里还会返回一个调用该方法的对象)
3、retainCount 返回当前对象的引用计数
手动内存管理黄金法则:
谁调用alloc\new\retain\copy\mutableCopy,谁就调用相应的release|autorelease;
当你需要某一个对象的时候,那么就给它发送一条retain方法。
当你不需要某一个对象的时候,那么就给它发送一条release方法。
内存管理不当产生的后果:
1、如果不再使用的对象没有被回收,就会照成内存泄露。会导致程序闪退。
2、如果正在被使用的对象被回收了,就会照成野指针错误。会导致程序崩溃。
内存管理中的关键字(@property)
retain在setter和getter方法中加入一些内存管理的代码(当属性是一个普通的OC对象的时候就使用retain)
assign表示的时直接复制,不会产生内存管理的代码(当属性是一个基本数据类型的时候就使用assign)
copy表示复制对象(当属性是NSString数据类型的时候就使用copy)
在@property中的关键字还有
生成多线程线程安全的关键字
nonatomic表示非原子的,不会生成线程安全的代码,速度比较快。iOS中的属性一般都是使用它。
atomic表示原子的,它会生成线程安全的代码。
控制权限的关键字
readonly表示可以读,只能生成getter方法。
readwrite表示可以读可以写,生成getter和setter方法的声明与实现。
修改方法名称的关键字
getter表示修改生成的getter方法的名称。一般情况下,BOOL类型的属性的getter方法都是以is开头的。
setter表示修改生成的setter方法的名称,不过一般不修改。
循环引用:
指针的是两个对象中,你中有我,我中有你。跟java中的一对一很相似。至于产生内存泄露的原因主要是相互之前强指针指着对方,感觉跟java里面谁来hibernate设置谁来管理对方。(在这里我们引入了强指针与弱指针在ARC中会提到,这里不做解释。)
解决循环引用的方式:让其中一方设置为assign。
多个对象之间不要封闭环,如果出现封闭的环,那么环中所有的对象将得不到释放。解决的方法,让其中一端为虚线。
自动释放池:(自动释放池是一个栈)
autorelease:延长对象的释放生命周期。作用:把对象放进离自己最近的那个自动释放池中。(它与对象在何地创建没有关系,只要标记上就放进离自己最近的那个自动释放池中。)
当自动释放池销毁的时候,它会把放在池中的所有对象进行一次release操作。
调用几次autorelease,在自动释放池销毁的时候就调用几次release操作。
在自动释放池中,只要是使用getter方法|构造方法返回来的对象都是放在池中。
ARC(automatic Reference Counting)自动内存管理
ARC中编译器的特性:编译器会在适当的时候,加入内存管理的代码。
(_strong强指针标识符,默认所有的指针都是强指针)
作用:只要强指针指向的对象,那么这个对象就不会被释放。
只要没有强指针指向的对象,那么这个对象将会被立即被释放。
(_weak弱指针标识符)
弱指针:不参与内存管理,对内存管理没有影响。不会影响对象的回收。
注意:不要使用一个弱指针指向一个刚刚创建出来的对象,一旦这样做,刚创建出来的对象马上销毁,在OC中也是自动销毁机制。
当出现循环引用的时候就必须要让一端使用弱指针。
在ARC中的@property
strong 对所有的普通OC对象
copy 对字符串
assign 基本数据类型
weak 对于循环应用的时候,必须保证一段使用的是weak。如果不这样你懂的。
ARC中的自动释放池机制:
只要是通过方法返回来的对象都是存放在ARC的自动释放池中。
以上的测试代码如下:
#import"Person.h"
#import "Room.h"
int main(int argc, const char * argv[]) {
// Person *person = [[Person alloc]init];
// long count = [person retainCount];
// NSLog(@"%zd",count);
// [person retain];
// NSLog(@"%zd",[person retainCount]);
// [person release];
// NSLog(@"%zd",person.retainCount);
// [person release];
// //测试OC中有没有空指针异常(发现系统不会报错,并且什么都没做)
// person = nil;
// NSLog(@"%zd",person.retainCount);
//多对象的内存管理
Person *person = [[Person alloc]init];//1
Room *room = [[Room alloc]init];//1
[person setRoom: room];//room = 2
[room release];// room = 1
room = nil;
[person release];// person =0 room =0;
person = nil;
return 0;
}
#import <Foundation/Foundation.h>
@class Room;
@interface Person : NSObject
@property(nonatomic,retain)Room *room;
//- (void)setRoom:(Room *)room;
@end
#import "Person.h"
#import "Room.h"
@implementation Person
//- (void)setRoom:(Room *)room{//这个跟@property(nonatomic,retain)Room *room生成的方法一致
// //成员对象一创建的时候,就会被初始化为nil
// //此处要加一个判断,判断两个对象是否是同一个对象。如果不相同,才需要内存管理的代码。
// if(_room != room){
// [_room release];
// _room = [room retain];
// }
[room retain];
_room = room;
// //等于(因为retain会返回对象)
_room = [room retain];
[_room release];
_room = [room retain];
//
//}
//永远不要试图自己调用这个方法
- (void)dealloc{
// [_room release];
// _room = nil;
//等价于
self.room = nil;//因为nil调用方法不会有任何变换。因此在上面的set方法中,不会让计数器加一
//它会在对象回收的时候被打印出来
NSLog(@"Person对象被回收了");
//这个方法中必须调用父类的该方法,并且必须放在最后
[super dealloc];
}
@end
/@interface Room : NSObject
@end
#import "Room.h"
@implementation Room
- (void)dealloc{
NSLog(@"Room对象被回收了");
//这个方法中必须调用父类的该方法,并且必须放在最后
[super dealloc];
}
@end