iOS内存管理—ARC

news/2024/7/20 15:47:07 标签: ios, 内存管理

文章目录

    • __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修饰符表示对对象的“强引用”。持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。

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

在这里插入图片描述


http://www.niftyadmin.cn/n/1617809.html

相关文章

iOS—手势操作

文章目录手势识别器—UIGestureRecognizer手势操作(拖动、捏合、旋转、点按、长按、轻扫)分别对应六个子类使用步骤1.创建手势识别器对象实例。创建时,指定一个回调方法。举例如下2.实现回调方法,设置相关属性3.添加到需要识别的 …

iOS—Blocks的实现

文章目录Block语法(7.26补充)Block类型变量 (7.26补充)Block的实质Block变量截获局部变量截获,值截获静态局部变量截获,指针截获全局变量、静态全局变量不截获,直接取值总结Block存储域_NSConcr…

iOS—从源码分析retain、release、retainCount实现

文章目录isa简单了解isa的bits成员变量isa的cls成员变量总结sideTables、sideTable 简单了解retainCount实现retain 实现release实现retain、release过程大总结:objc_autoreleaseReturnValue实现objc_retainAutoreleasedReturnValue实现内容会陆续补充isa简单了解 …

iOS—对象的本质(学习笔记)

只是小小记录一下,内容不完整 OC底层实现是c\c,oc的面向对象都是基于c\c实现NSObject c 底层结构 struct NSObject_IMPL {//CLass 指针,64位占8个字节__unsafe_unretained Class isa; };//Class typedef struct objc_class *Classoc中的定义…

iOS—[self class]和[super class]

先上一段代码 Person类继承NSObject Student类继承Person //Person类 继承自NSObject #import <Foundation/Foundation.h>interface Person : NSObjectend#import "Person.h"implementation Personend//Student类 继承自Person #import "Person.h"N…

iOS—属性关键字

文章目录property/synthesize/dynamic系统默认关键字原子性读写权限引用计数assignunsafe_unretainedweakstrongcopy一些问题property/synthesize/dynamic property 我们经常会看到 属性 getter setter 成员变量 但实际上property会生成setter、getter的方法声明synthesize会…

iOS—strong、weak的实现以及dealloc的调用流程

文章目录__strong修饰符ARC在编译器、运行期做了哪些工作&#xff1f;__weak修饰符__weak在使用过程中调用的函数objc_initWeakstoreWeakSideTableweak_table_t 结构weak_entry_t 结构weak_register_no_lock方法&#xff0c;添加弱引用weak_entry_for_referent &#xff0c;获取…

iOS—KVO用法及本质

文章目录KVOKVO 的使用KVO本质总结一些问题KVO KVO全称KeyValueObserving&#xff0c;键值监听&#xff0c;是苹果提供的一套事件通知机制。允许对象监听另一个对象特定属性的改变&#xff0c;并在改变时接受到事件。 KVO 的使用 三个方法 注册观察者 addObserver:(nonnull…