文章目录
- __strong
- __weak修饰符
- __unsafe_unretained修饰符
- __autoreleasing修饰符
- 非显示使用__autoreleasing修饰符
- 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修饰符表示对对象的“强引用”。持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。
- 附有__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_;
}
-
__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块结束前对象存在。
- 访问附有__weak修饰符的变量
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