OC 课程笔记总结11-内存管理2:autorelease 和 ARC

 课程:内存管理

autorelease 的基本使用:

在 oc 中,autorelease 是半自动释放,不是完全自动释放。但是如果打开了 ARC 机制,那么是不允许在使用 autorelease 这个方法的。使用了 ARC 之后,所有手动进行内存管理的方法都不会允许使用,包括retain,release。autorelease 方法的返回值类型是 id,返回对象本身。一般是和新建对象时的代码统一写在一起。如下: 类 *指针变量 = 【【【类 alloc】init】 autorelease】;把一个对象的分配空间,初始化,释放都写在一起。记住 autorelease 返回的是对象本身,然后赋值给指针变量。但是 autorelease 并不是完全自动释放对象,而是会将对象放入一个自动释放池中,即autorelease pool 。其实自动释放池也是一个对象,一切皆对象,是对象就会被销毁,当自动释放池被销毁时,会对池中的所有对象做一次 release 操作,注意的是只做一次操作并且只做 release 操作,并不是把所有的对象都释放,即自动释放池被释放后,池中的对象并不一定跟着销毁,即这个自动释放池只是代替了对每个新建对象时 alloc 再写一句 release并且位置顺序必须正确这一过程,注意这点。

自动释放池是一个对象,那么这个对象也需要去创建,创建释放池的格式如下:

@autoreleasepool

{ // 从大括号开始就表示创建了自动释放池

//  把要进行内存管理的对象放到这个括号内中,并调用 autorelease 方法就能够把对象放入自动释放池了。即在这个大括号内就不用再写对象 release 这句代码了。

}     //大括号结束,就表示自动释放池销毁了,类似于函数一样,函数执行完毕就是大括号结束。


可以创建无线多个自动释放池,并且释放池可以嵌套使用,在调用 autorelease 之后,对对象的计数器值没有马上作用,只有当程序执行到释放池的大括号结束时,才会 对池内的所有对象的计数器release一次。

多个释放池是以栈的形式在内存中存放的,栈,即先进后出,后进先出,这里的栈指的是一种数据结构。不是指栈内存,是一种数据结构。栈数据结构表示最先进入栈的对象最后一个退出栈的。对于嵌套的释放池,第一个释放池放在最底部,然后放入第二个对象池,然后放入第三个对象池,。。。。第 N 个对象池,当释放池释放时,则是一种出栈动作,即最后进栈的第一个出栈,那么最内层的释放池最先释放,然后依次往外层释放。所有 autorelease 并不是自动释放,这个功能只不过是延迟了对象释放的时间 ,由系统强制把 release 的时间放在了大括号结束之后。同时,这个 autorelease 也有自己的缺点,就是不能够精确的手动控制对象的 release 时间,在实际开放中,通常是需要进行精确控制的,所以 autorelease 只适合占用内存较小的对象使用,对于占用内存多的对象,还是需要手动进行对对象释放的精确控制。通常对象中属性基本都是基本数据类型,这样的对象通常是占用内存较小的对象,如果对象的属性中又包含了其他对象,那么这个对象所占用的内存就会较大,需要手动控制。

 autorelease 使用常见错误:

首先一定要明确在 对象调用了 autorelease 方法后,尽管没有立即进行一次 release 操作,但最终还是进行一次 release 操作的,所以只要出现了 autorelease 也相当于进行了一次 release 操作。当大括号结束时,在释放池中有多少个 autorelease,就是做多少次 release 操作,这个要注意,一个大括号可以对应多个 autorelease。如果不把对象放在自动释放池中,调用 autorelease 方法是不会有任何作用的,因为不知道在什么时候才是大括号结束,所以不知道什么时候才 release。并且在ios程序运行的过程中,系统会自动随机创建无数个自动释放池,但是系统什么时候创建是无法预知的,是随机的。这些自动释放池都是放在栈中,并且,对于每个释放池而言,只要执行到了自己的大括号结束,这个池子就会马上销毁,即这个池子在栈中出栈了,然后程序会继续往下执行。ios 程序运行时不仅会随机创建释放池,还会随机销毁一些释放池,一般是当用户进行了点击屏幕的操作时,系统会随机销毁一些释放池,所以对于释放池何时被销毁,不需要程序员去关注。当点击按钮或者屏幕时程序出现闪退的话有可能就是某些对象的内存管理出错了。

autorelease 也属于内存管理方法中的一种,所以使用时也一样要按照内存管理的原则去执行:只要 alloc 或者 retain,都要 release 或者 autorelease,谁 alloc(retain),谁就 release 或者 autorelease,不要去管别的对象的计数器操作,只要对自己的计数器操作负责就行,自己引用加1了,就一定要减1. 出现 alloc 或者 retain 是只代码可以见,是看得见有这两个方法被调用是才需要 release 或者 autorelease,对于不同文件的代码而言,不需要关心别的方法中是否使用了 alloc,也不用去猜测,只有当本文件中的方法中明确的写了 alloc'或者 retain 这两个方法之后,才需要进行-1操作,对于调用别方法而言,进行或者不进行-1操作是由别的方法决定的,所以,只需要对看的见的 alloc 和 retain进行-1操作,其他看不见的都不需要去关心。


autorelease 在实际开放中的应用:

在实际的开发过程中,通常会编写一些类方法,能够直接返回一个已经调用了autorelease 对象,而不用再在新建每一个对象时都要写一次 autorelease,格式如下:

未创建 便捷的类方法时这么写:

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

创建一个返回 autorelease 对象的类方法:

声明:+ (id) person      //   返回类型设置为 id,并且这个类方法的方法名和类名一致,除了首字母不大写,方法名和属性名首字母都不大写。并且系统自带的类方法都是以类名开头的(NS 不算在内),这是良好的代码习惯。oc 中是不允许任何重名的方法的,即使是在不同的文件夹中也不允许。所以通常在命名时加上前缀,NS 就是一种前缀,

实现:

+ (id) person

{

return   [[Person alloc] init] autorelease];

}


然后在创建新对象时,直接调用类方法即可:

Person *p = [Person person];            //  类名调用类方法。

还可以根据实际需要在这个创建新对象的方法中加入更多的功能,比如初始化这个对象时可以传入一些参数来对其中的某些属性赋值,然后再返回,这样可以少写更多的重复代码,因此可以 根据实际需求来灵活编写。这样的方法通常命名为:

类名 With参数名,这样命名是官方提倡的命名规范,系统自带的类方法也是按这样的命名方法命名的。


对于字符串对象,创建方法为 : NSString *str = @"hello ios"; 在这个新建对象的过程中,并没有明确的写有使用了 alloc 方法,所以也不需要去 release。一定要记住,没有明确的看的见的 alloc 和 retain,都不需要去考虑这次 release。

对于一些系统自带的方法中,如果方法名没有明确的 alloc 和 retain,那么说明这些方法都是不需要去手动 release 的,这些方法都是 autorelease 返回的对象。

对于使用便捷返回方法时,要注意一些细节,比如说返回值时要注意返回的是哪个类。在实际编写代码中可以灵活的使用 self 来完善代码。使得代码的复用性更高,父类可以用这个方法,子类也可以用这个方法,灵活的写 self 就能达到这个目的。

所以在创建类方法时,多使用 self 而不是类名,这样就可以提高代码的复用性,对于当前类和子类都可以使用。

如果运行时出现错误: unrecognized selector sent to instance  。这是一个典型错误。说明了某个对象调用了一个不存在的方法,也就是所提示的无法识别的 selector 发送给了一个实例对象。方法即消息,消息即 selector。 


内存管理总结:

一。计数器的基本操作。

1.retain

2.release

3.retainCount 

二。set 方法的内存管理。

1.set方法中要首先进行判断,然后再+1-1操作。

2.dealloc 方法的重写,要对引用过的对象-1操作,要把【super dealloc】 放在最后,并且 dealloc 方法是由系统自动调用的,不能够手动调用,只能是系统自动调用。

三。自动生成内存管理代码的@property

1.4类关键字要记住,其中如果是基本数据类型, assign 在默认时也要写上去,并且有一个参数是一定要写的:nonatomic。setter 和 getter 较少使用,readwrite和 readonly 也较少使用。

2.被 retain 过的属性,必须在 dealloc 中 release 一次。

四。autorelease

1.当方法名中没有 alloc,new,copy 这些明显字眼时,这些方法返回的对象都是经过 autorelease 的,不需要再手动-1

2.创建类方法中,不要直接使用类名,要灵活使用 self。



ARC:Automatic Reference Counting.

ARC 是一种编译器特性,即当编译器发现是retain 或者其他+1操作时,会自动在 dealloc 中加入一个 release 操作,不用再手动加入,并且发现一个 alloc 时,也会自动加上一个 release,这些都是由编译器来完成的,所以 ARC 实际是一个编译器特性,而不是垃圾回收机制,这一点和 java 中是不同的。java 中的垃圾回收机制是在程序的运行过程中检测某个对象是否被使用,如果没有就回收,而 OC 中的 ARC 是编译器在编译时自动生成某些代码的功能,类似 property,在新版本的 xcod'e 中,默认都是使用 ARC 来进行内存管理,而不用手动操作了,如果使用 ARC,凡是和内存管理相关的方法都不能够再调用,例如 release,retain,retainCount,autoreleas,并且在重写 dealloc 中不再允许使用【super dealloc】这句代码。

ARC 的原理:ARC 是如何知道什么时候应该 release 呢?这是由 ARC 的判断准则来决定的:只要没有任何的强指针指向某个对象,那么 ARC 就会马上释放对象。指针实际上分为两种:强指针,弱指针。在默认情况下,所有指针都是强指针。在函数执行完毕后,函数内部的所有局部变量,即基本数据类型,包括指针都被销毁,当指针销毁时,如果某个对象没有任何的强指针指向的话,那么这个对象就会跟着被销毁。

弱指针:使用修饰词“__weak”,修饰的指针就是弱指针,注意的是不要加上@或者#,格式如下:__weak 类型 *指针变量 ;那么这个指针就是弱指针。当只有弱指针指向对象时,ARC 也会立即释放这个对象,并且 ARC 还会对弱指针进行清理的操作,如果不清空弱指针,弱指针也会引起野指针错误。

如果使用了 ARC 机制,那么 retain 是会被禁止使用的,因此在@property 参数中,也不能在使用 retain 的参数,取而代之的是 strong 和 weak 这两个参数,如果属性是一个对象时,那么使用 property 自动生成方法时如果使用了 strong 参数,那么就会将这个指针定义为强指针,默认也是强指针,但是和 assign 一样,即使是默认的,也要写上去。

要区分指向和被指向的不同,指向是指向别人,被指向是被别人指向,ARC 中判断的标准是是否有被强指针指向,即是否被指向。

ARC 特点:

1.不允许调用 retain,release,retainCount;

2.允许重写 dealloc,但是不允许调用【super dealloc】;

3.@property 的参数:retain 不能在使用,改为使用

* strong : 类似于原来的 retain;

* weak : 相当于不进行+1操作,类似于 assign 的效果,即直接赋值,不会对计数器的值产生影响,但是是用在 OC 对象上。而关键字 assign 还是能够继续使用的,对应的是基本数据类型。

4.以前使用 retain 的地方都改用 strong,其他的都不用变。


如何对旧项目进行 ARC 转换:

在 xcode 的功能中,可以使用重构的功能,有编译器来进行更改。但是有些第三方框架是不支持 ARC 转换的。如果调用不支持 ARC 的框架,很有可能报错。通过设置可以让 ARC 和非 ARC 的文件共同存在与一个项目中。使用命令:-fno-objc-arc,将这句代码加入编译文件的描述中。但是现在的新项目基本都是 ARC了。如果是整个项目不支持 ARC,但是又想某个文件是 ARC,同样是对编译文件进行描述,-f-objc-arc,对需要 ARC 的文件加入这句描述后就能够让这个文件支持 ARC 了。


在 ARC 中也有循环引用的问题,类似于使用 两个类都是使用 retain 就会造成循环引用,解决办法就是一个使用 retain,另一个使用 assign,同理,在使用了 ARC 之后,retain 不能再使用,使用 strong 也会造成循环引用的问题,解决办法就是一个用 strong,一个用 weak。解决的思想就是 相互引用的两个类必须现有一个类要被释放,否则这两个类都不会被释放。






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值