ARC规则

作者: 香菜那么好吃为什么不吃香菜 | 来源:发表于2020-07-16 18:25 被阅读0次

概要

ARC即为“自动引用计数”,自动的帮我们处理引用计数的相关部分。

在编译单位上,我们可以设置ARC是有效或者无效,甚至可以对每个文件选择是否使用ARC。一个应用程序中可以混合ARC有效或者ARC无效的二进制形式哦。

内存管理的思考方式

上文中提到的内存管理的四种思考方式在ARC有效时也是可行的。引用计数式内存管理的思考方式就是思考ARC所引起的变化

*自己生成的对象,自己持有

*非自己生成的对象,自己也能持有

*不需要自己持有对象的时候释放

*释放非自己持有的对象

所有权修饰符

ARC有效的时候,id类型和对象类型同C语言其他类型不同,其类型上必须附加所有权修饰符。

所有权修饰符有:

_strong

_weak

_unsafe_unreatained

_autoreleasing

_strong修饰符

_strong修饰符表示对对象的“强引用”,持有强引用的变量在超出其作用域的时候被废弃,随着强引用的失效,引用的对象会随之释放。

_strong修饰符是id类型和对象类型默认的所有权修饰符,即:

{
//下面两句话是等同的
id obj = [[NSObject alloc] init]; 
id _strong obj = [[NSObject alloc]init];//自己生成并持有对象

//因为变量obj为强引用,所以自己持有对象
}
//因为变量obj超出其作用域,强引用失效,所以自动的释放自己持有的对象,对象的所有者不存在,因此废弃该对象

此外,对象的所有者和对象的生存周期是明确的,那么在取得非自己生成并持有的对象的时候处理如下

{
  id _strong obj = [NSMutableArray array];//取得非自己生成并持有的对象
  //因为变量obj为强引用,所以自己持有对象
  }
//因为变量obj超出其作用域,强引用失效,所以自动释放自己持有的对象

两种情况下,对象的所有者和对象的生存周期都是明确的

有关强引用的赋值

id _strong obj0 = [[NSObject alloc]int];//obj持有对象A的强引用
id _strong obj1 = [[NSObject alloc]init];//obj1强引用对象B
id _strong obj2 = nil;//obj2不持有任何对象
obj0 = obj1;//obj0持有由obj1持有的对象B的强引用,因为obj0被赋值,所以原先持有的对象A的引用失效。对象A的所有者不存在,因此废弃对象A。 此时,对象B的强引用变量为obj0和obj1

obj2 = obj0;//obj2持有由obj0赋值的 对象B的强引用,此时持有对象B的强引用的变量为obj0,obj1,obj2

obj0 = nil;此时持有对象B的强引用的变量为obj1,obj2

obj1 = nil;此时持有对象B的强引用的变量为obj2

obj2 = nil;对象B的所有者不存在,因此废弃对象

_strong修饰符的变量,不止在变量作用于中,在赋值上也能够正确地管理其对象的所有者

OC类成员变量也可以在方法参数上,使用附有_strong修饰符的变量

@interface Test : NSObject
{
  id _strong = obj_;
}
- (void) setObject:(id _strong)obj;

@implementation Test
- (id) init
{
  self = [super init];
  return self;
}
- (void) setObject : (id _strong)obj
{
  obj_ = obj;
}

@end
{
  id _strong test = [[Test alloc]init]; //test持有Test对象的强引用
  [test setObject : [[NSObject alloc]int]; //test对象的obj成员持有对NSObject对象的强引用
}
//因为test超出其作用域,强引用失效,自动释放Test对象。Test对象所有者不存在,因此废弃该对象
//废弃Test对象的同时,Test对象的_obj成员也会被废弃。
//NSObject对象的强引用失效,自动释放NSObject对象,NSObject对象的所有者不存在,因此废弃该对象

_strong修饰符和后面要说的_weak,_autoreleasing修饰符都可以保证附有这些修饰符的自动变量初始化为nil

id _strong obj0;
id _weak obj1;
id _autoreleasing obj2;
id _strong obj0 = nil;
id _weak obj1 = nil;
id _autoreleasing = nil;

上边两部分代码实现结果是一样的

通过_strong修饰符,不必在此输入release和retain。因为id类型和对象类的所有权修饰符默认为_strong,使ARC有效简单的编程遵循了OC内存管理的思考方式。

·前两项“”自己生成的对象自己持有“以及”非自己生成的自己持有“只需通过对带_strong修饰符的变量赋值便可以达成。
·通过废弃带_strong修饰符的变量(变量作用域结束或者成员变量所属对象废弃)或者对变量进行赋值,都可以做到”“不再需要自己持有的对象释放掉”。
·最后一项“非自己持有的对象不可释放”,现在不会再键入release,也就不用担心啦。


_weak修饰符

_strong修饰符看起来能辅助编译器完美的进行内存管理了,但是你还记得一个叫做“循环引用”的可怕词语吗?带_strong修饰符的成员变量在持有对象的时候很容易发生循环引用的。所以还是需要我们的_weak修饰符。

@interface Test : NSObject
{
  id _strong = obj_;
}
- (void) setObject:(id _strong)obj;

@implementation Test
- (id) init
{
  self = [super init];
  return self;
}
- (void) setObject : (id _strong)obj
{
  obj_ = obj;
}

@end
//以下为循环引用
{
  id test0 = [[Test alloc]init];//对象A
  id test1 = [[Test alloc]init];//对象B
  [test0 setObject:test1];
  [test1 setObject:test0];
}
//因为test0变量超出其作用域,强引用失效,所以自动释放Test对象A
//因为test1变量超出其作用域,强引用失效,所以自动释放Test对象B
//此时,持有Test对象A的强引用变量为Test对象B的obj_
//此时,持有Test对象B的强引用变量为Test对象A的Obj_
//发生内存泄露(应当废弃的对象在超出其生存周期后继续存在)
{
//以下为自引用,也是循环引用
id test = [[Test alloc]int];
[test setObject:test];//对自身的强引用
}

_weak修饰符提供弱引用,弱引用不会持有对象

先来看一行代码
id _weak obj = [[NSObject alloc]int];
编译器会对这行代码发出警告,此源代码将自己生成并持有的对象赋值给带有_weak修饰符的变量obj,即变量obj持有对持有对象的弱引用,生成的对象会被立即释放。

如果将对象先赋值给带有_strong修饰符修饰的变量后再赋值给附有_weak修饰符的变量,就不会有警告了

{
  id _strong obj0 = [[NSObject alloc]init];//自己生成并持有对象,obj0自己持有对象
  id _weak obj1 = obj0;//obj1变量持有生成对象的弱引用
}
//因为obj0变量超出其作用域,强引用失效,所以自动释放自己持有的对象
//对象的所有者不存在,因此废弃该对象

那我们来看一下弱引用是怎么解决循环引用的吧

对于上述例子,我们可以将类成员变量变为附有_weak修饰符,就可以避免循环引用

@interface Test : NSObject
{
  id _weak = obj_;
}
- (void) setObject:(id _strong)obj;

_weak修饰符还有一个大优点哦~那就是:在持有某对象的弱引用的时候,若该对象被废弃,则此弱引用将自动失效并且被自动赋值为nil(空弱引用)。
那就可以通过检查_weak弱引用是不是为nil,来判断对象是不是被废弃了~

遗憾的是,_weak修饰符只能用于IOS5以上以及OS X Lion以上版本的应用程序,在低版本中可以使用_unsafe_unretained代替,他在引用对象的时候不会持有对象的,但是当引用的对象不存在的时候,_unsafe_unretain并不会自动被赋值给nil,所以有可能崩溃。


_autoreleasing修饰符

·ARC有效的时候,通过指定@autoreleasepool块来替代“NSAutoreleasePool”类对象生成、持有、以及废弃这一范围。
·ARC有效的时候,可以通过将对象赋值给附加了_autoreleasing修饰符的变量来替代调用autorelease方法。对象赋值给附有_autoreleasing修饰符的变量等价于在ARC无效时调用对象的autorelease方法,即对象被注册到autoreleasepool。也就是说,在ARC有效的时候,用@autorelease块代替NSAutoreleasePool类,用附有_autoreleasing修饰符的变量来替代调用autorelease方法。

_autorelease的非显示调用

实际情况下我们见到_autoreleasing 修饰符和见到_strong的情况一样罕见,那我们来看看非显示调用autorelease方法的情况吧~
取得非自己生成并持有的对象时,如同以下源代码,虽然可以使用alloc/new/copy/mutableCopy以外的方法来取的对象,但是该对象已经被自动注册到了autoreleasepool里面了。这里和ARC无效的时候取得调用了autorelease方法的对象是一样的效果。这是为什么呢?

由于编译器会检查方法名是否以alloc/new/copy/mutableCopy开头,如果不是则自动将返回值对象注册到autoreleasepool中。

@autoreleasepool{
  id _strong obj = [NSMutableArray array];
  //因为变量obj默认为强引用,所以自己持有对象
  //该对象由编译器判断方法名之后,自动注册到autoreleasepool
 }
//变量obj超出其作用域,强引用失效,所以自动释放自己持有的对象
//随着autoreleasepool块的结束,注册到其中的所有对象被释放,因为对象的所有者不存在,废弃对象
+ (id) array
{
  return [[NSMutableArray alloc]init];
}
+ (id) array
{
  id obj =  [[NSMutableArray alloc]init];
  return obj;
}

由于return使得对象变量超粗其作用域,所以该强引用对应的自己持有的对象会被自动释放,但该对象作为函数的返回值,编译器会自动将其注册到autoreleasepool。

访问到_weak变量时,必定访问到autoreleasepool中的对象

这是为什么呢?

_weak修饰符只持有对象的弱引用,而在访问引用对象的过程中,该对象可能被废弃如果把要访问的对象注册到autoreleasepool中,那么在@autoreleasepool块结束之前都能确保该对象存在。因此,在使用附有_weak修饰符的变量时就必定要使用注册到autoreleasepool中的对象。

id _autoreleasing *obj

id指针或者对象的指针在没有显示指定的情况下会被附加上_autoreleasing修饰符

规则

在ARC有效的时候,必须遵循以下规则

1 不能使用retain/release/retainCount/autorelease

2 不能使用NSAllocateObject/NSDeallocateObject

3 必须遵守内存管理的命名规则

4 不要显示调用dealloc

dealloc方法中也无需调用 [super alloc];这一步会自动执行

5 使用@autoreleasepool块代替NSAutoreleasePool

6 不能使用区域(NSZone)

7 对象型变量不能作为C语言结构体(struct/union)的成员

C语言的规约上没有方法来管理结构体成员的生存周期。因为ARC 把内存管理的工作分配给编译器,所以编译器必须能够知道并管理对象的生存周期。例如C语言的局部变量就可以用该变量的作用域管理对象,但是对于C语言的结构体成员来说,这在标准上就是不可实现的。
要把对象型变量加入到结构体成员中,可强制转换为void *或者是附加前面所述的_unsafe_unretain修饰符(不属于编译器内存管理对象,如果管理时不注意赋值对象的所有者,便有可能遭遇内存泄露或程序崩溃)

8 显示转换“id”和“void*”

id型或者对象型变量赋值给void *或者逆向赋值时都需要进行特定的转换。

如果只想单纯的赋值,则可以使用_bridge转换

id obj = [[NSObject alloc]init];
void *p = (_bridge void *)obj;
id o = (_bridge id)p;

转换容易但是问题多啊~转换为void *的_bridge转换,这样转换的安全性与赋值给_unsafe_unretained修饰符相近,甚至更低。如果管理的时候不注意赋值对象的所有者,就会因垂直指针而导致程序崩溃。
(那转换为id的_bridge转换呢?若没特指,就是强引用赋值,但是不会加入autoreleasepool,需要手动release)
_bridge_retained转换可使要转换赋值的变量也持有所赋值的对象

//ARC有效
//_bridge_retained转换变为了retain。变量obj和变量p同时持有该对象
void *p = 0;
{
  id obj = [[NSObject alloc]init];
  p = (_bridge_retained void*)obj;
}

作用域结束的时候,虽然随着持有强引用的变量obj失效,对象随之释放 ,但是由于_bridge_retained转换使变量p看上去持有该对象,因此对象不会被废弃。

_bridge_transfer转换:被转换的变量所持有的对象在该变量被赋值给转换目标变量之后随之释放

实现oc对象和CF对象的转换
CFTypeRef CFBridgingRetain(id X) {
  return (_bridge_retained CFTypeRef)X;
}
id CFBridgingRelease (CFTypeRef X) {
  return (_bridge_transfer id)X;
}

属性

相关文章

  • ARC规则-规则

    所有内容引用自《Objective-C 高级编程 iOS与OS X多线程和内存管理》,加入了自己的部分理解。 本节...

  • ARC规则

    title: ARC规则date: 2016-03-17 16:49:05categories: iOStags:...

  • ARC

    设置ARC有效的编译命令: -fobjc-arc Xcode 4.2 默认设定为对所有的文件 ARC 有效 规则:...

  • ARC环境下的内存管理

    一、ARC强制实施的新规则 为了工作,ARC强加了一些在使用其他编译器模式时不存在的新规则。这些规则旨在提供一个完...

  • iOS面试题整理(一)

    1.ARC MRC GC (1)ARC 自动技术管理 ARC的规则就是只要对象没有强指针引用,就会被释放掉,换而言...

  • OC高级编程iOS内存管理-第1章-自动引用计数

    自动引用计数 什么是自动引用计数内存管理/引用计数ARC规则ARC的实现 1.1 什么是自动引用计数 ARC和MR...

  • 内存管理 - ARC规则

    ARC有效时,所有权修饰符一共有4种: * __strong 修饰符* __weak 修饰符* __unsafe_...

  • iOS-ARC

    本文的内容包括 一、所有权修饰符二、ARC的基本规则三、ARC的实现 ARC中仍然是通过引用计数来管理内存,这个本...

  • IOS内存管理(一)基本概念与原理

    文章结构 1.内存管理的基本规则2.autoreleasePool3.ARC管理方法3.1 ARC 引入的四个ow...

  • iOS ARC有效时必须遵守的规则

    ARC有效必须遵守的规则: 不能使用 retain/release/retainCount/autorelease...

网友评论

      本文标题:ARC规则

      本文链接:https://www.haomeiwen.com/subject/ahaphktx.html