黑马程序员-OC加强回顾-内存管理
来源:互联网 发布:大疆精灵4pos数据导出 编辑:程序博客网 时间:2024/05/14 18:03
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、内存管理的基本概念及范围
1、 由于移动设备的内存极其有限,所以每个app占用的内存也是有限制的。
2、OC内存管理的范围:
管理任何继承NSObject的对象,对其他的基本数据类型无效。
基本数据类型数据占用空间一般在栈区,而对象类型是程序运行过程中动态分配的,存储在堆区
内存管理主要是对堆区中对象的内存管理。
二、内存管理的原理及分类
1、内存管理的原理
1)对象的所有权
任何对象都可能有一个或多个所有者。
只要对象至少还拥有一个所有者,它就会继续存在。
2)、引用计数器:
每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象。
对象刚被创建时,默认计数器值为1。当计数器为0时,对象被销毁。
3)、引用计数器的作用:
判断对象要不要回收的唯一依据。
但是如果对象值为nil时,计数器为0,但不回收。
4)、引用计数器的操作
给对象发送消息,进行相应地计数器操作。
retain消息:使计数器+1,该方法返回对象本身
release消息:使计数器-1(并不代表释放对象)
retainCount消息:获得对象当前的引用计数器值 %ld %tu
5)对象的销毁
当一个对象的引用计数器为0时,那么它将被销毁,其占用的内存被系统回收。
当对象销毁的时,系统会自动想对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关资源,dealloc就像是对象的“临终遗言”。
一旦对象被回收了,那么它所占的存储空间就不能再使用,坚持使用会导致系统崩溃(野指针)。
2、OC内存管理分类
Objective-C提供了三种内存管理方式:
MRC ARC 垃圾回收
需要理解MRC,但实际使用时尽量ARC
三、手动内存管理(MRC)
1、 要使用手动内存管理,首先要关闭ARC
2、重写dealloc方法
1)一定要[super dealloc],而且要放到最后
意义:先释放子类占用的空间再释放父类占用的空间
2)对self(当前)拥有的其他对象做一次release操作
- (void)dealloc{ //1 先释放子类自己的对象的空间 NSLog(@"Person已经挂了"); //2 再释放父类的 [super dealloc];}
四、内存管理的原则
1、内存管理的原则
1)原则
只要还有人在使用某个对象,那么这个对象就不会被回收
2)谁创建,谁release
3)谁retain,谁release
4)曾经让某个对象计数器加1,就应该让其在最后减1 。
2、内存管理研究内容
1)野指针:
(1)定义的指针变量没有初始化
(2)指向的空间已经被释放了
2)内存泄露:
{ Person *p = [Person new]; } p 栈区 [Person new]; 堆区
如果栈区的p已经释放了,而堆区的空间还没有释放,堆区的空间就被泄露了
五、单个对象内存管理(野指针)
野指针:访问一块坏得内存(已经被回收的,不可用的内存)
僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。(默认状态下,Xcode为了提高编码效率,不会时时检查僵尸对象。)
注意:
1)空指针:没有指向任何东西的指针,给空指针发送消息并不会报错。
nil Nil NULL的区别
nil:指向OC对象
Nil:指向OC类
NULL:通用指针(泛型指针)
2)不能用[p retain]让僵尸对象复活。
3)也指针操作,一个指针指向的空间被释放,这个指针就成了僵尸对象,不能继续操作僵尸对象。
六、单个对象内存管理
1)避免使用僵尸对象的方法:在释放了对象指向的空间后,设置对象为nil。
2)对象的内存泄露
(1)retain和release个数不匹配,导致内存泄露。
(2)对象使用过程中被赋值了nil,导致内存泄露。
Dog *d = [[Dog alloc] init]; //1 d = nil; [d eat]; //nil eat [d release]; // nil release
(3)在函数或者方法中不当的使用retain或者release造成的问题
-(BOOL)compareColorWithOther:(Dog*)dog{ [dog retain]; //让传入的对象的引用+1 return YES;
七、多个对象内存管理
1、多个对象的野指针问题(知识点)
有两个类,存在关联关系,Car的对象是Person对象的实例变量。
Person类的声明#import <Foundation/Foundation.h>#import "Car.h"@interface Person : NSObject{ Car *_car; //实例变量,和Person是存在关联关系}-(void)goLasa;-(void)setCar:(Car *) car;@endPerson类的实现#import "Person.h" @implementation Person - (void)dealloc{ NSLog(@"人已经挂了"); [super dealloc];} -(void)goLasa{ [_car run]; }-(void)setCar:(Car *) car{ _car = car; }@endCar类的声明#import <Foundation/Foundation.h> @interface Car : NSObject{ //定义实例变量 int _speed;}//声明一个对象方法-(void)run;-(void)setSpeed:(int) speed;@endCar类的实现#import "Car.h" @implementation Car- (void)dealloc{ NSLog(@"车已经毁了"); [super dealloc];}-(void)setSpeed:(int) speed{ _speed = speed; }-(void)run{ NSLog(@"车以%d码的速度奔向拉萨",_speed); }@end
问题是:
在主函数中分别创建Person类和Car类的对象,但是如果先释放Car的对象,那么Person类的对象的中的实例变量_car就成了野指针,不能再调用run方法。
2、多个对象的内存泄露问题
在上面的例子中,为了避免野指针操作,可以改进代码。
-(void)setCar:(Car *) car{
_car = [car retain];
}
这样可以避免野指针操作,但是去无法释放car对象,造成了内存泄露
3、多个对象的内存管理
解决办法:
改进dealloc方法,让Person释放之前,先释放car。
- (void)dealloc{ [ _car release]; NSLog(@"人已经挂了"); [super dealloc];}
八、set方法内存管理
set方法的内存管理的原则
如果在一个类中,有其他类的对象(关联关系)
set方法书写的时候,要先判断是否是同一个对象,如果不是,先release旧值,在retain新值。
1、原对象无法释放造成的内存泄露
如果一个Person对象和两个Car对象有关联关系,其中一个可以释放,而另一个无法释放的问题。
Person *p = [Person new]; Car *bmw = [Car new]; bmw.speed = 100; p.car = bmw; [p driveCar]; Car *byd = [Car new]; byd.speed = 80; p.car = byd; [p driveCar]; [byd release]; [p release];结果:2015-11-10 15:00:32.742 9-set方法的内存管理[780:63952] 车正在以100的速度往前开!2015-11-10 15:00:32.743 9-set方法的内存管理[780:63952] 车正在以80的速度往前开!2015-11-10 15:00:32.743 9-set方法的内存管理[780:63952] 速度为80的car dealloc!2015-11-10 15:00:32.743 9-set方法的内存管理[780:63952] Person dealloc!Program ended with exit code: 0
第一个car没有释放,造成内存泄露。
2、原对象可以释放,但是在set自己的时候又出现僵尸对象访问。
修改set方法,先释放上一个car对象,在赋值新的。
-(void)setCar:(Car *) car{ //为了解决set方法的内存泄露,我们在新的retain之前,先release旧值 [_car release]; _car = [_car retain];}
3、判断新传过的对象是否是原来对象,如果不是,先release,再retain。
if (_car != car) { [_car release]; _car = [car retain];
总结:
1、对于基本类型数据作为实例变量
int _speed; set方法的写法 -(void)setSpeed:(int)speed{ _speed = speed; }
2、对于对象作为另外一个类的实例变量
-(void)setDog:(Dog*)dog{ //1)判断对象是否是原对象 if(_dog != dog){ //2) release旧值 [_dog release]; // retain 新的值,并且赋值给实例变量 _dog = [dog retain]; } }
九、@property参数
1、@property的修饰关键字
1)控制set方法的内存管理
(1)retain:release旧值,retain新值,要配合nonatomic使用,用于OC对象。
@property(nonatomic,retain) Car *car;-(void)setCar:(Car *)car{ if(_car != car){ [_car realase]; _car = [car retain]; } }
(2)assign:直接赋值,不做任何内存管理(默认,用于非OC对象类型)。
assign 直接赋值 -(void)setCar:(Car *)car{ _car = car; }
(3)copy:release旧值,copy新值(一般用于NSString *)。
2、控制是否需要生成set方法
1)readwrite:同时生成set方法和get方法(默认)。
2)readonly:只会生成get方法。
@property (nonatomic,assign,readonly) int tuiNum;
3、多线程管理
1)atomic:性能低(默认),对属性加锁,多线程下线程安全。
2)nonatomic:性能高(为iOS系统开发软件建议使用,为mac开发软件可以使用atomic)。
对属性不加锁,多线程下不安全。
4、控制set方法和get方法的名称
1)setter:设置set方法的名称,一定有个冒号。
2)getter:设置get方法的名称。
@property (nonatomic,assign,setter=isVip:,getter=isVip) BOOL vip;[p isVip:YES]; //调用set方法 if (p.isVip) //调用get方法 { NSLog(@"这时VIP客户"); }
十、@class用法
1、为什么要使用@class
可以简单的引用一个类,只是告诉编译器这是一个类,并不会包含类里的所有内容。
好处:如果引用的类内部发生改变,而不需要重新编译,可以提高效率。
2、具体用法
在.h文件中使用@class引用一个类
在.m文件中使用#import包含这个类的.h文件
如在A类中引用B类。
代码如下:
A.h文件中 @class BA.m文件中#import “B.h”
3、特殊用法:两个类互相引用。
一个用#import引用,另一个用@class引用。
4、面试题
#import和@class的区别
1、作用上的区别
1)#import会包含引用类的所有信息(内容),包括引用类的变量和方法。
2)@class仅仅是告诉编译器有这么一个类,不包括类里地具体信息。
2、效率上的区别
如果很多头文件都通过#import引用了同一个类,当这个类发生改变的时候,引用它的所有类都要重新编译一遍,效率非常低。但是使用@class就不会这样,所以效率相对较高。
十一、循环retain问题
1、循环retain的场景
比如A对象retain了B对象,B对象retain了A对象 循环retain的弊端 这样会导致A对象和B对象永远无法释放
有Dog和Person两个类Dog类的声明#import <Foundation/Foundation.h>@class Person;@interface Dog : NSObject@property (nonatomic,retain) Person *owner;@endPerson类的声明#import <Foundation/Foundation.h>@class Dog;@interface Person : NSObject//人拥有一条狗@property (nonatomic,retain) Dog *dog;@end主函数int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [Person new]; //1 Dog *d = [Dog new]; //1 //人有一条狗 p.dog = d; d.owner = p; [p release]; [d release]; } return 0;}结果是p和d都没有释放。
2、循环retain的解决方案
当两端互相引用时,应该一端用retain、一端用assign
十二、NSString类的内存管理问题
1、NSString类的内存特点
使用字符串的时候:@“” stringWithString alloc initWithString 这三种格式都是在常量区
如果你需要的字符串在常量去已经存在了,就不会再分配新的内存空间
还有其他几种格式会存在堆区。
对于字符串的引用计数器是一个非常大值,无法用一个release就释放掉其内存空间。
十三、autorelease基本使用
1、自动释放池
一种特殊的栈结构
2、自动释放池的创建方式
@autorelease{ //自动释放池的开始代码块;} //销毁自动释放池
3、autorelease
是只用支持引用计数的内存管理方式
它会在自动释放池销毁的时候对池子里的每个对象发送一次release消息。
注意:
这里只是发送一次release消息,如果对象的引用计数还不为0,则对象依然不会被释放。
4、使用autorelease的好处
1)不需要关心对象释放的时间
2)不需要关心什么时候调用release。
5、autorelease基本用法
1)将对象放到一个自动释放池中
2)当自动释放池销毁时,会对池子里的所有对象发送一次release消息
3)会返回对象本身
4)调用完autorelease方式后,对象的计数器不受影响。
int main(int argc, const char * argv[]) { //1 创建自动释放池 Person *p = [Person new]; // p 1 @autoreleasepool {//自动释放池开始 [p run]; NSLog(@"%lu",p.retainCount); // 1 // [p autorelease] 把对象p加入到自动释放池中 // 注意:加入到自动释放池中以后, 引用计数不会变化 [p autorelease]; //加入自动释放池, NSLog(@"%lu",p.retainCount); // 1 [p run]; }//自动释放池结束 [p release]; [p run]; //此时就是僵尸对象访问了,会报错。 return 0;}
6、autorelease的原理
autorelease只是把release的调用延迟了,当自动释放池销毁的时候,池子里地对象才会调用release方法。
- 黑马程序员-OC加强回顾-内存管理
- 黑马程序员--学习OC加强内存管理
- 黑马程序员----oc加强笔记----内存管理
- 黑马程序员-OC加强-内存管理
- 黑马程序员—IOS加强视频—oc内存管理
- 黑马程序员——oc加强学习(内存管理)
- 黑马程序员-OC-内存管理
- 黑马程序员--oc:内存管理
- [黑马程序员][OC]内存管理
- 黑马程序员-OC内存管理
- 黑马程序员---OC--内存管理
- 黑马程序员---oc 内存管理
- 黑马程序员-OC-内存管理
- 黑马程序员-OC内存管理
- 黑马程序员——OC语言加强---内存管理的基本概念及范围
- ——黑马程序员——OC加强部分 内存管理
- 黑马程序员 _9 OC基础 内存管理
- 黑马程序员 OC------内存管理学习
- [原]横向滑动的HorizontalListView滑动指定位置的解决方法
- typedef int (*init_fnc_t)(void);的理解
- java定时器Timer当执行时间大于间隔时间会怎样
- 32/64位操作系统&&应用程序详解(英文资料)
- hdu 3033 I love sneakers!
- 黑马程序员-OC加强回顾-内存管理
- java 异常在实际应用中的总结
- 光棍节pairs引发的血案
- Intent 拨打电话
- 用java开发跨平台简易服务器站点监控程序
- 《Java JDK8学习笔记》读书笔记(2)
- Python基本语法及优缺点
- 我们来谈谈Android回调.
- 【Java】List的subList方法