1.为什么要内存管理?
2.OC是如何管理内存的?
1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 { 5 int _age ; 6 } 7 @property int age; 8 @end
1 #import "Person.h" 2 3 @implementation Person 4 // 当一个Person对象被回收的时候,就会自动调用这个方法 5 6 - (void)dealloc 7 { 8 NSLog(@"Person对象被回收"); 9 // super的dealloc一定要调用而且放在最后面 10 // EXD_BAD_ACCESS:访问了一块坏的内存(已被回收和释放的内存) 11 // 野指针 12 [super dealloc]; 13 } 14 @end
1 int main() 2 { 3 /* 创建Person对象 */ 4 Person *p = [[Person alloc] init]; 5 /* 获取对像的计数器初始值 */ 6 NSInteger c = [p retainCount]; 7 NSLog(@"计数器初始值 %ld",c); //值为1 8 9 // 2.调用retain方法,计数器加1,retain方法返回的是对象本身 10 [p retain]; 11 c = [p retainCount]; 12 NSLog(@"retain之后计数器值 %ld",c); //值为2 13 // 减为0才会被回收 14 15 // 调用alloc 调用return 就必须有release 16 [p release]; // 减一操作 17 c = [p retainCount]; 18 NSLog(@"release后计数器值 %ld",c); //值为1 19 [p release]; // 减一操作之后值为0,系统回收内存,执行dealloc方法,挂掉后面也不能在加1 20 21 22 // 给已经释放的对象发送了一条setAge消息: 报错 23 //p.age = 20; 24 25 //防止野指针 26 p = nil; 27 28 // 不能多次释放,产生野指针,OC里面指向僵尸对象(不可用内存) 29 // EXD_BAD_ACCESS:访问了一块坏的内存(已被回收和释放的内存) 30 // 野指针 31 [p release];//给空指针发消息不会报错,OC不存在空指针错误 32 33 return 0; 34 }
运行结果:
2015-03-23 21:02:45.239 内存管理-引用计数器的操作[1521:161738]计数器初始值 1
2015-03-23 21:02:45.241 内存管理-引用计数器的操作[1521:161738] retain之后计数器值 2
2015-03-23 21:02:45.241 内存管理-引用计数器的操作[1521:161738] release后计数器值 1
2015-03-23 21:02:45.241 内存管理-引用计数器的操作[1521:161738] Person对象被回收
1 -(void)setAge:(int)age 2 { 3 _age = age; 4 }
1 - (void)setCar:(Car *)car 2 { 3 // 1.先判断是否是新传来的对象 4 if (_car != car) 5 { 6 // 2.旧对象做一次release 7 [_car release]; // 第一次的话_car为空, 8 //对空对象release不会报错 9 // 3.新对象做一次retain 10 _car = [car retain]; 11 } 12 }
1 // 1.一定要[super dealloc];而且放到最后面 2 // 2.对这个对象self(当前)所拥有的其他对象做一次release 3 - (void)dealloc; 4 { 5 [self->_car release]; 6 //当你挂掉时必须让你拥有的对象的计数器减一 7 NSLog(@"%d 岁的人对象被回收",self->_age); 8 [super dealloc]; 9 }
4. 使用@property自动创建对象的set方法时如何处理内存管理?
retain : release旧值retain新值(适用于OC对象)
assign : 直接赋值(默认:适用于非Oc对象类型)
copy : release 旧值copy新值
例:@property (retain) NSString *name
2>>是否要生成set方法
有种情况是,某个变量是只读的不提供设置值的方法;
readwrite: 同时生成set get方法(默认)
readonly:只生成get声明、实现
例:@property (readwrite,assign) int age;
3>>多线程管理
nonatomic:性能高(一般就用这个)
atomic:性能低(默认)
例:@property (nonatomic,assign) int age;
4>>set和get方法名称
setter :决定set方法名称,一定要有冒号, 默认是setAge:
getter:决定get方法名称(一般用在BOOL类型)
常用于改变返回值BOOL类型的get方法的名称,一般以is开头.
例:@property (getter = abc,setter = setAbc: ) int weight;
get方法叫做abc
返回值BOOL类型的方法名称,一般以is开头
@property (getter = isRich) BOOL rich; // 这种在开发中常见
1. autorelease基本用法
1》会将对象放到一个自动释放池中
2》挡自动释放池销毁时,会对池子里面的所有对象做一次release操作
3》会返回对象本身
4》调用完release后对象计数器并不会改变,只有到池子结束时才会release
5》池子存储在栈中
2. autorelease 好处
1》不用在关心对象释放的时间
2》不在关心什么时候调用release
3. autorelease
1》占用内存较大的对象不要随便使用Release
2》占用内存较小的对象使用autorelease,没有太大影响
4.错误写法
1》alloc之后调用了autorelease,又调用了release
1 @autoreleasepool 2 3 { 4 5 // 调用两次autorelease,就会调用两次release出现野指针错误 6 7 Person *p = [[[[Person alloc] init] autorelease]; autorelease]; 8 9 [p release]; 10 11 }
2》连续调用对次release
1 Person *p = [[[[Person alloc] init] autorelease] autorelease];
5. 自动释放池
1》在IOS程序运行过程中会创建无数的池子。这些池子都已栈的方式存在先进后出
2》当一个对像调用autorelease方法时,会将这个对象放到栈顶释放池
autorelease使用示例:#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic,assign) int age; @end
1 #import "Person.h" 2 3 @implementation Person 4 - (void)dealloc 5 { 6 NSLog(@"Person 对象被回收"); 7 [super dealloc]; 8 } 9 @end
1 int main() 2 { 3 // autorelease 返回对象本身 4 // 调用完release后对象计数器并不会改变,只有到池子结束时才会release 5 // sutorelease 作用,会将对象放到自动释放池中,当释放池被销毁时会将 6 // 池子中所有的对象做一次release操作,池子在哪 7 @autoreleasepool 8 {// { 代表释放池开始 9 Person *p =[[[Person alloc] init] autorelease]; 10 p.age = 10; 11 12 13 // 可创建n多个自动释放池 14 @autoreleasepool 15 { 16 Person *p =[[[Person alloc] init] autorelease]; 17 p.age = 20; 18 19 } 20 //[p release]; 21 22 }// {代表释放池销毁 23 24 return 0; 25 }
2015-03-23 21:52:55.539 autorelease[1627:174759] Person对象被回收
2015-03-23 21:52:55.540 autorelease[1627:174759] Person对象被回收
可见在地址池结束时确实释放了我们创建的对象。四.循环引用的内管管理
1 #import <Foundation/Foundation.h> 2 3 // 不用import 4 // @class 仅仅告诉编译器card是一个类 5 @class Card; 6 7 @interface Person : NSObject 8 9 @property (nonatomic,retain) Card * card; 10 @end
3.关于@class
1》使用 @class 类名; 就可以引用一个类,说明一下它是一个类
2》 @class 和#import的区别
#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;
@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息.
如果有n多个文件都#import了同一个头文件,那么如果最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率肯定是很慢的,而使用@class方式就不会出现这种问题
在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类
Person类的实现(使用了#import)
1 #import "Person.h" 2 // 在.m文件中用到才包含 3 #import "Card.h" 4 5 @implementation Person 6 - (void)dealloc 7 { 8 NSLog(@"Person 对象被回收"); 9 // 回收之前将拥有的对象计数器减一 10 [_card release]; 11 [super dealloc]; 12 } 13 @end
1 #import <Foundation/Foundation.h> 2 3 // 仅在声明时告诉编译器这是一个类 4 @class Person; 5 6 @interface Card : NSObject 7 /* 生分证上的 人属性*/ 8 // 由于Person类是对象所以使用retain参数 9 @property (nonatomic,retain) Person *person; 10 @end
1 - (void)dealloc 2 { 3 NSLog(@"Card对象被回收"); 4 // 回收之前将拥有的对象计数器减一 5 [_person release]; 6 [super dealloc]; 7 } 8 @end
会引起什么问题?
1 #import <Foundation/Foundation.h> 2 #import "Person.h" 3 #import "Card.h" 4 int main() 5 { 6 // 创建Person类的对象 7 Person *p = [[Person alloc] init]; 8 // 创建Card 类的对象 9 Card *c = [[Card alloc] init]; 10 11 // 使Person对象拥有Card对象 12 p.card = c; 13 c.person = p; 14 // 使Card对象拥有Person对象 15 16 [c release]; 17 [p release]; 18 19 return 0; 20 }
上面的代码看似没有什么问题,但是没有调用person对像和Card对象的dealloc方法,说明两个对象都没有被释放?
分析原因:
p、c创建时计数器都为1,执行完p.card = c,之后c的计数器为2,同理c.peron= p之后p的计数器也为2,继续执行后面的[c release],c的计数器为1,不为空就不会执行card的dealloc方法,就不会使他拥有的person对象计数器减一,那么p的计数器还是2,之后执行[p relase]p计数器变为1,也不会执行person的dealloc,这样程序运行完毕,哪个对象都没有释放。
3.解决方案
当两端互相引用时,应该一端用retain、一端用assign
先修改card端修改之后如下:
1 2 3 #import <Foundation/Foundation.h> 4 5 // 仅在声明时告诉编译器这是一个类 6 @class Person; 7 8 @interface Card : NSObject 9 /* 生分证上的 人属性*/ 10 11 // 由于Person类是对象所以使用retain参数 12 //@property (nonatomic,retain) Person *person; 13 // 修改之后的代码 14 @property (nonatomic,assign) Person *person; 15 @end 16 17 #import "Card.h" 18 #import "Person.h" 19 @implementation Card 20 21 - (void)dealloc 22 { 23 NSLog(@"Card对象被回收"); 24 // 回收之前将拥有的对象计数器减一 25 // [_person release]; 26 [super dealloc]; 27 } 28 @end
2015-03-23 22:38:42.620 循环引用[1729:186447] Person对象被回收
2015-03-23 22:38:42.621 循环引用[1729:186447] Card对象被回收