黑马程序员---iOS基础---内存管理

本文详细介绍了Objective-C的内存管理,包括内存管理的概念、对象的引用计数、内存管理原则,以及手动引用计数(MRC)和自动引用计数(ARC)的使用。讨论了野指针错误、僵尸对象和内存泄露等问题,并讲解了autorelease的工作原理和使用场景。
摘要由CSDN通过智能技术生成

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


1.内存管理的概念

由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,

app可用的内存是被限制的,如果一个app使用的内存超过20M,则系统会向该app发送Memory Warning消息。

收到此消息后,需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等,否则程序会崩溃。

管理范围:

管理任何继承NSObject的对象,对其他的基本数据类型无效。

本质原因是因为对象和其他数据类型在系统中的存储空间不一样,

其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,

指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。


任何对象都可能拥有一个或多个所有者。只要一个对象至少还拥有一个所有者,它就会继续存在

Person *p = [[Person alloc] init];

Cocoa所有权策略

任何自己创建的对象都归自己所有,可以使用名字以“alloc”或“new”开头或名字中包含“copy”的方法创建对象,可以使用retain来获得一个对象的所有权。

对象的引用计数器

每个OC对象都有自己的引用计数器,是一个整数表示对象被引用的次数,即现在有多少东西在使用这个对象。

对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁。

在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器。


判断对象要不要回收的唯一依据(存在一种例外:对象值为nil时,引用计数为0,但不回收空间)就是

计数器是否为0,若不为0则存在。


给对象发送消息,进行相应的计数器操作。

retain消息:使计数器+1,该方法返回对象本身

release消息:使计数器-1(并不代表释放对象)

retainCount消息:获得对象当前的引用计数器值 


当一个对象的引用计数器为0时,那么它将被销毁,其占用的内存被系统回收。

当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的“临终遗言”。

一旦重写了dealloc方法就必须调用[superdealloc],并且放在代码块的最后调用(不能直接调用dealloc方法)。

一旦对象被回收了,那么他所占据的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误)。


1) 如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除非整个程序已经退出)

2)任何一个对象, 刚生下来的时候, 引用计数器都为1。

(对象一旦创建好,默认引用计数器就是3)当使用alloc、new或者copy创建一个对象时,对象的引用计数器默认就是1


2.Objective-C提供了三种内存管理方式

MannulReference Counting(MRC,手动管理,在开发iOS4.1之前的版本的项目时我们要自己负责使用引用计数来管理内存,

比如要手动retain、release、autorelease 等,而在其后的版本可以使用ARC,让系统自己管理内存。)

automatic reference counting(ARC,自动引用计数,iOS4.1 之后推出的)

garbage collection(垃圾回收)。iOS不支持垃圾回收;

ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存;

开发中如何使用:需要理解MRC,但实际使用时尽量用ARC


重写dealloc方法:

(1)一定要[superdealloc],而且要放到最后,意义是:先释放子类占用的空间在释放父类占用的空间

(2)对self(当前)所拥有的的其他对象做一次release操作

-(void)dealloc{

[Car release];

[super dealloc];

}

永远不要直接通过对象调用dealloc方法(实际上调用并不会出错)

一旦对象被回收了, 它占用的内存就不再可用, 坚持使用会导致程序崩溃(野指针错误)为了防止调用出错,可以将“野指针”指向nil(0)。


@interface Person : NSObject

@property int age;

@end


@implementation Person

-(void)dealloc{

NSLog(@“对象被销毁!”);

[super dealloc];

}

@end


Person *p = [[Person alloc ] init];

NSLog(@“P->retainCount = %ld”,p.retainCount);//1

Person *p1 = [Person new];

NSLog(@“P1->retainCount = %ld”,p1.retainCount);//1

[p retain];

NSLog(@“P->retainCount = %ld”,p.retainCount);//2

[p release];

NSLog(@“P->retainCount = %ld”,p.retainCount);//1

[p release];//retainCount  = 0; 输出对象被销毁


3.内存管理原则

只要还有在使用某个对象,那么这个对象就不会被回收;

只要想使用这个对象,那么就应该让这个对象的引用计数器+1;

当不想使用这个对象时,应该让对象的引用计数器-1;

(1)如果通过alloc,new,copy来创建了一个对象,那么就必须调用release或者autorelease方法

(2)不是你创建的就不用你去负责

只要你调用了retain,无论这个对象时如何生成的,你都要调用release


4.单个对象的野指针问题


野指针错误:访问了一块坏的内存(已经被回收的,不可用的内存)。

僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。

默认情况下xcode为了提高编码效率,不会时时检查僵尸对象,打开僵尸对象检测。

空指针:没有指向任何东西的指针,给空指针发送消息不会报错

不能使用[pretain]让僵尸对象起死复生。


5.单个或者多个对象内存管理


为了防止不小心调用了僵尸对象,可以将对象赋值nil(对象的空值)。

retain 和release个数不匹配,导致内存泄露

对象使用的过程中被赋值了nil,导致内存泄露

在函数或者方法中不当的使用retain 或者relase 造成的问题


set方法的内存管理:

1)基本数据类型:直接赋值

-(void)setAge:(int)age{
_age=age;
}

2)OC对象类型

-(void)setCar:(Car*)car{

//1.先判断是不是新传进来的对象

If(car!=_car){

//2对旧对象做一次release

[_car   release];//若没有旧对象,则没有影响

//3.对新对象做一次retain
_car=[car   retain];
}

}


6.@calss的使用


作用

可以简单地引用一个类

简单使用

@class Dog; //类的引入

仅仅是告诉编译器: Dog是一个类; 并不会包含Dog这个类的所有内容

具体使用

在.h文件中使用@class引用一个类

在.m文件中使用#import包含这个类的.h文件

通常引用一个类有两种办法:

一种是通过#import方式引入;另一种是通过@class引入;

我们实际开发中尽量在.h头文件中使用@class

#import和@class的区别:

作用上的区别

#import会包含引用类的所有信息(内容), 包括引用类的变量和方法

@class仅仅是告诉编译器有这么一个类, 具体这个类里有什么信息, 完全不知

效率上的区别

如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,

后面引用到这个文件的所有类都需要重新编译一遍, 编译效率非常低。

相对来讲,使用@class方式就不会出现这种问题了


7.autorelease基本使用

自动释放池

(1)在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。

(2)当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中

(1)iOS5.0以前的创建方式

NSAutoreleasePool  *pool= [ [ NSAutoreleasePool   alloc]  init];
`````````````````
[pool release];//[pool  drain];用于mac

(2)iOS5.0以后

@autoreleasepool

{//开始代表创建自动释放池

·······

}//结束代表销毁自动释放池


OC的内存管理机制中比较重要的一条规律是:谁申请,谁释放

考虑这种情况,如果一个方法需要返回一个新建的对象,该对象何时释放?

方法内部是不会写release来释放对象的,因为这样做会将对象立即释放而返回一个空对象;

调用者也不会主动释放该对象的,因为调用者遵循“谁申请,谁释放”的原则。那么这个时候,就发生了内存泄露。


使用autorelease的好处

(1)不需要再关心对象释放的时间

(2)不需要再关心什么时候调用release


基本用法

(1)会将对象放到一个自动释放池中

(2)当自动释放池被销毁时,会对池子里的所有对象做一次release

(3)会返回对象本身

(4)调用完autorelease方法后,对象的计数器不受影响(销毁时影响)

autorelease原理:

autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease

系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。

对于autorelease pool本身,会在如下两个条件发生时候被释放

1)手动释放Autorelease pool

2)Runloop结束后自动释放

对于autorelease pool内部的对象

在引用计数的retain == 0的时候释放。release和autorelease pool 的drain都会触发retain--事件。














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值