iOS内存管理—ARC

ARC有效时,对象类型必须附加所有权修饰符,所有权修饰符一共四种

  • __strong修饰符
  • __weak修饰符
  • __unsafe_unretained修饰符
  • __autoreleasing

__strong

  • __strong修饰符是id类型和对象类型默认的所有权修饰符。
    在没有明确指定所有权修饰符时,默认为__strong
id obj = [[NSObject alloc] init];
//实际为
id __strong obj = [[NSObject alloc] init];
  • 附有__strong修饰符的变量obj在超出其变量作用域时,即在该变量被废弃时,会释放被赋予的对象
//代码中明确指定变量作用域
{
	id obj = [[NSObject alloc] init];
}


//ARC无效时,该代码可记述为
{
	id obj = [[NSObject alloc] init];
	[obj release];
}
  • __strong修饰符表示对对象的“强引用”。持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。

11

  • 附有__strong修饰符的变量之间可以相互赋值,__strong修饰符的变量,不仅在变量作用域中,在赋值上也能够正确的管理其对象的所有者。如图:

在这里插入图片描述

  • __strong修饰符、__weak修饰符、__autoreleasing修饰符可以保证将附有这些修饰符的自动变量初始化为nil

__weak修饰符

使用__weak修饰符可以避免循环引用
循环引用容易发生内存泄漏。所谓内存泄漏就是应当废弃的对象在超出其生存周期后继续存在。
在这里插入图片描述
类成员变量的循环引用如图
在这里插入图片描述
对象持有自身时,也会发生循环引用即自引用,如图
在这里插入图片描述

__weak修饰符,提供弱引用。弱引用不能持有对象实例。
生成的对象会立即被释放
在这里插入图片描述

可以改为:

{
	id __strong obj = [[NSObject alloc] init];
	
	id __weak obj3 = obj;
	//obj3持有对象生成对象的弱引用
}
//obj变量超出其作用域,强引用失效,自动释放自己持有的对象
//对象的所有者不存在,所以废弃该对象

避免循环引用,可将可能发生循环引用的类成员变量改为附有__weak修饰符的成员变量
即:

@interface Test: NSObject {
	id __weak obj_;
}

1

  • __weak修饰符另一优点,在持有对象的弱引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值状态(空弱引用)
    这一点区别于__unsafe_unretained
    在这里插入图片描述
    在这里插入图片描述

    • (补充) 遇到的问题
      在这里插入图片描述
      打印结果如下:
      在这里插入图片描述
      查看汇编可以看到在执行完第二个nslog,会执行objc_release,所以可以推测到,为了保证在打印过程中__weak修饰的变量的对象不被释放,在打印前,打印前增加其引用计数,打印完再release一次。
      在这里插入图片描述

__unsafe_unretained修饰符

__unsafe_unretained修饰符是不安全的所有权修饰符。
ARC式内存管理是编译器的工作,附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。
附有__unsafe_unretained修饰符的变量同附有__weak修饰符的变量一样,因为自己生成并持有的对象不能继续为自己所有,所以生成的对象会立即被释放。
**唯一区别于__weak的是,__unsafe_unretained在对象销毁后并不会置空,这时附有__unsafe_unretained的变量就是一个悬垂指针。**使用该变量则可能造成崩溃。
🌰1:
在这里插入图片描述
🌰2:
在这里插入图片描述

__autoreleasing修饰符

ARC中不能使用autorelease方法,也不能使用NSAutoreleasePool类
在这里插入图片描述

在这里插入图片描述

但在ARC中,autorelease功能是其作用的

//ARC无效时
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	id obj = [[NSObject alloc] init];
	[obj autorelease];
	[pool drain];

//相当于 ARC有效时的以下代码
@autoreleasepool {
               
 	id __autoreleasing obj3 = [[NSObject alloc] init];
    //ARC中,将对象赋值给附有__autoreleasing修饰符的变量
    //等价于,在ARC无效时调用对象的autorelease方法,[obj3 autorelease];
	//即对象被注册到autoreleasePool
    }

非显示使用__autoreleasing修饰符

    • 获取非自己生成并持有的对象时,根据方法名
      编译器会检查方法名是否以alloc/new/copy/mutableCopy开始,如果不是则自动将返回值的对象注册到autoreleasepool。

//array方法中obj作为函数的返回值,编译器会自动将其注册到autoreleasepool
+ (id)array {
	id obj = [NSMutableArray alloc] init];
	return obj;
}

@autoreleasepool {
	//由于ARC默认带有__strong, arr为强引用,持有nsarray对象
	id arr = [NSArray array];
}
//超出作用域,arr强引用失效,自动释放自己持有的对象
//同时,@autoreleasepool块的结束,注册到autoreleasepool中的所有对象自动释放

//此时,NSarray对象没有持有者,所以废弃对象

具体看这篇

    • 访问附有__weak修饰符的变量
      访问附有__weak修饰符的变量必须访问注册到autoreleasepool的对象。
      __weak修饰符只持有对象的弱引用,而在访问引用对象的过程中,该对象有可能被废弃,如果把要访问的对象注册到autoreleasepool中,可以确保在@autoreleasepool块结束前对象存在。
id __weak obj = obj0;
NSLog(@"%@", [obj class]);

//相当于
id __weak obj = obj0;
id __autoreleasing tmp = obj;
NSLog(@"%@", [tmp class]);

(补充)以上是高级编程里的内容,在后续的实践中发现这部分存在问题。。
在这里插入图片描述
按着书中的说法,访问__weak修饰的变量obj1,那么此时就会将对象注册到autoreleasepool中,但是打印释放池,池子里并没有内容…
在这里插入图片描述

  • id的指针或对象的指针在没有显示指定时会被附上__autorelease修饰符
//id相当于void *,id *也可以理解为指针的指针
id *obj 相当于 id __autorelease *obj

//指向NSObject对象的指针的指针
NSObject **obj 相当于 NSObject *__autorelease *obj

ARC规则

在ARC有效的情况下,须遵守的规则

  • 不能使用retain/release/retainCount/autorelease
  • 不能使用NSAllocateObject/NSDeallocateObject

(在高级编程这本书里GNUstep的alloc实现实际是直接调用NSAllocateObject函数生成并持有对象的)

  • 须遵守内存管理的方法命名规则

以alloc、new、copy、nutableCopy开始的方法返回对象时,必须返回给调用方所应当持有的对象。在ARC有效时,也是这样,同时也要加一条命名规则
以init开始的方法,必须是实例方法必须返回对象,返回的对象并不注册到autoreleasepool上,基本上就是对alloc方法返回值的对象进行初始化并返回对象

  • 不能显式调用dealloc

在ARC中,不能调用dealloc,在重写dealloc时,不要调用超类的实现即[super dealloc],可以释放对象的实例变量以外的资源,不要直接发送dealloc消息,对象的dealloc方法是由运行时调用的

  • 使用@autoreleasepool块代替NSAutoreleasePool

在ARC中,NSAutoreleasePool不可用

  • 不能使用区域(NSZone)

不管ARC是否有效,区域在现在的运行时系统中已单纯地被忽略

  • 对象型变量不能作为C语言结构体的成员

在ARC中把内存管理的工作分配给了编译器,所以编译器必须能够知道并管理对象的生存周期,在C语言中,这一要求并不能实现。但是可以通过两种方法将对象型变量添加到结构体成员中,一种是强制转换为void*类型,一种是用__unsafe_unretained修饰变量(__unsafe_unreatined修饰的变量不属于编译器的内存管理对象)。

  • 显示转换id和void *

Objective-C指针类型’id’到C指针类型’void *'的隐式转换需要桥接类型转换


属性

属性声明的属性与所有权修饰符的对应关系
在这里插入图片描述
需要注意的一点是,当同时声明了属性和成员变量,成员变量所带有的所有权修饰符一定要和属性声明中的属性一致。
//对于__weak属性“obj”,现有的实例变量“_obj”必须为__weak

在这里插入图片描述

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值