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。解决的思想就是 相互引用的两个类必须现有一个类要被释放,否则这两个类都不会被释放。






















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: iOS内存管理版本记录如下: 1. iOS 2.0及更早版本:使用手动管理内存的方式。 2. iOS 3.0:引入了基于引用计数的自动内存管理,使用retain和release函数来增加或减少对象的引用计数。 3. iOS 5.0:引入了ARC(自动引用计数)机制,ARC会在编译时自动插入retain和release代码,减少手动管理内存的工作。 4. iOS 7.0:引入了内存诊断工具Memory Usage Report,可以监测App内存使用情况,帮助开发者优化内存管理。 5. iOS 8.0:引入了一些新的API,如NSCache和NSURLSession,使得内存管理更加方便和灵活。 6. iOS 11.0:引入了基于图片大小的UIImage渲染机制,减少了内存占用。 7. iOS 13.0:引入了叫做“Scene”的多任务环境,使得内存管理更加复杂,需要更加小心谨慎地处理内存问题。 总的来说,随着iOS版本的不断更新,内存管理的机制也在不断地完善和优化,使得iOS应用能够更加高效地使用内存,提高用户体验。 ### 回答2: iOS的内存管理是由操作系统自动管理的,在不同的版本中有所不同。 在iOS 5之前的版本中,内存管理主要依赖于手动管理引用计数(reference counting)来管理对象的生命周期。开发者需要手动调用retain和release方法来增加或减少对象的引用计数,以确保对象在不再需要时能够被正确释放。这种方式需要开发者非常谨慎地管理对象的引用,以避免内存泄漏或野指针等问题。 从iOS 5开始,iOS引入了自动引用计数(Automatic Reference Counting,ARC)的内存管理机制。ARC可以自动地插入retain、releaseautorelease等方法的调用,使得开发者不再需要手动进行内存管理。开发者只需要关注对象的创建和使用,而不需要关心具体的内存管理细节。ARC减少了内存管理的工作量,提高了开发效率,并且减少了内存泄漏和野指针等问题的发生。不过,ARC并不是完全的自动化内存管理,开发者仍然需要遵循一些规则,比如避免循环引用等,以保证内存的正确释放。 随着iOS版本的不断更新,苹果不断改进和优化内存管理机制。每个新版本都带来了更好的性能和更高效的内存管理。开发者可以通过关注苹果的官方文档和开发者社区中的更新内容来了解每个版本中的具体变化和改进。 总结来说,iOS的内存管理从手动的引用计数到自动引用计数的演变,极大地简化了开发者的工作,并提高了应用的性能和稳定性。随着不断的改进和优化,iOS的内存管理会越来越高效和可靠。 ### 回答3: iOS内存管理版本记录是指苹果公司在不同版本的iOS操作系统中对于内存管理方面的改进和更新记录。随着iOS版本的不断迭代,苹果在内存管理方面进行了一系列的优化和改进,以提高系统的稳定性和性能。 首先,在早期的iOS版本中,苹果采用了手动内存管理的方式,即开发人员需要手动创建和释放内存,容易出现内存泄漏和内存溢出等问题。为了解决这些问题,苹果在iOS5版本中引入了自动引用计数(ARC)机制。ARC机制能够通过编译器自动生成内存管理代码,避免了手动管理内存带来的问题。 其次,iOS6版本引入了内存分页机制。这个机制能够将应用程序内存分成不同的页,将不常用的页置于闲置列表中,从而释放出更多的内存空间。这些闲置列表中的页能够在需要时快速恢复到内存中,减少了内存压力。 此外,iOS7版本中进一步提升了内存管理的能力。苹果在这个版本中引入了内存压缩技术,将内存中的数据进行压缩,从而提高了内存利用率。此外,iOS7还引入了资源清理功能,可以自动清理不再使用的资源,释放内存空间。 最后,在iOS13版本中,苹果进一步改进了内存管理策略。该版本中引入了后台内存优化功能,能够自动优化应用在后台运行时的内存占用,减少了后台应用对于系统内存的占用和影响。 综上所述,iOS内存管理版本记录反映了苹果在不同版本的iOS操作系统中对于内存管理方面的改进和优化。这些改进和优化使得iOS系统更加稳定和高效,并且提升了应用程序的性能和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值