内存管理(二) ARC

上篇我们介绍了MRC,本篇我们介绍下ARC

ARC概述

ARC是一种编译器功能,它通过LLVM编译器和Runtime协作来进行自动管理内存。LLVM编译器会在编译时在合适的地方为 OC 对象插入retain、release和autorelease代码来自动管理对象的内存,从而彻底解放程序员。

ARC不能解决的问题

  • Block等引发的循环引用问题(更多循环引用看我这篇文章)
  • 底层 Core Foundation 对象需要手动管理

所有权修饰符

__strong
__weak
__unsafe_unretained
__autoreleasing

在对象变量的声明中使用所有权修饰符时,正确的格式为:

 ClassName * 所有权 varName;

例如:

Person * __weak yang
Person * __unsafe_unretained yang

其它格式在技术上是不正确的,但编译器会 “原谅”。也就是说,以上才是标准写法。

__strong

默认修饰符,_strong 修饰符表示对对象的“强引用”,只要有强指针指向对象,对象就会保持存活。

id strongObj = [[NSObject alloc] init];
id __strong strongObj = [[NSObject alloc] init];

与之相反 如果没有强引用指针指向对象,对象就会死亡。
或者可以这么理解 最少有一个强引用指针指向对象,对象才不会死亡。

__weak

__weak 修饰符表示对对象的“弱引用” 不影响对象的释放

__weak经常会这么用

 id __strong strongObj = [[NSObject alloc] init];id __weak weakObj = strongObj;

在解释这段代码前,我们先看一个极端的特例,能加深我们对ARC 强持有对象规则的理解

__weak极端例子

- (void)viewDidLoad {[super viewDidLoad];id __weak weakObj = [[NSObject alloc] init];NSLog(@"%@", weakObj);
}

编译器警告
Assigning retained object to weak variable; object will be released after assignment

输出

weakObj===(null)

单纯地使用__weak修饰符修饰变量,编译器会给出警告,因为NSObject的实例创建出来没有强引用,就会立即释放,ARC环境中一个对象必须有一个强引用指针指向它来保证自己存活。 这里没有强引用指针所以对象被创建出来之后就销毁了
而_weak修饰的指针 weakObj,会在所指向的NSObject对象被释放后,自动指向nil,所以打印为nil

汇编分析

    0x10f5ade30 <+16>:  movq   0x75a9(%rip), %rdi        ; (void *)0x00007fff80030660: NSObject// 创建NSObject 对象0x10f5ade37 <+23>:  callq  0x10f5ae402               ; symbol stub for: objc_alloc_init0x10f5ade3c <+28>:  leaq   -0x18(%rbp), %rcx0x10f5ade40 <+32>:  movq   %rcx, %rdi0x10f5ade43 <+35>:  movq   %rax, %rsi0x10f5ade46 <+38>:  movq   %rax, -0x30(%rbp)0x10f5ade4a <+42>:  movq   %rcx, -0x38(%rbp)// 创建weak指针变量0x10f5ade4e <+46>:  callq  0x10f5ae420               ; symbol stub for: objc_initWeak0x10f5ade53 <+51>:  movq   0x21b6(%rip), %rcx        ; (void *)0x00007fff20191530: objc_release0x10f5ade5a <+58>:  movq   -0x30(%rbp), %rdi0x10f5ade5e <+62>:  movq   %rax, -0x40(%rbp)0x10f5ade62 <+66>:  callq  *%rcx0x10f5ade64 <+68>:  movq   -0x38(%rbp), %rdi
->  0x10f5ade68 <+72>:  callq  0x10f5ae426               ; symbol stub for: objc_loadWeakRetained0x10f5ade6d <+77>:  movq   %rax, %rcx0x10f5ade70 <+80>:  leaq   0x21a9(%rip), %rdi        ; @"weakObj===%@"0x10f5ade77 <+87>:  xorl   %edx, %edx0x10f5ade79 <+89>:  movq   %rax, %rsi0x10f5ade7c <+92>:  movb   %dl, %al0x10f5ade7e <+94>:  movq   %rcx, -0x48(%rbp)0x10f5ade82 <+98>:  callq  0x10f5ae3e4               ; symbol stub for: NSLog0x10f5ade87 <+103>: jmp    0x10f5ade8c               ; <+108> at ViewController.m0x10f5ade8c <+108>: movq   -0x48(%rbp), %rdi// 没有强引用指针能让对象存活 所以对象销毁0x10f5ade90 <+112>: callq  *0x217a(%rip)             ; (void *)0x00007fff20191530: objc_release0x10f5ade96 <+118>: leaq   -0x18(%rbp), %rdi// 对象被销毁之后 objc_destroyWeak被调用 销毁weak指针 weak设置为nil 0x10f5ade9a <+122>: callq  0x10f5ae41a               ; symbol stub for: objc_destroyWeak

正常的weak使用

    id __strong strongObj = [[NSObject alloc] init];id __weak weakObj = strongObj;

NSObject的实例已有强引用,再赋值给__weak修饰的变量就不会有警告了

__weak弱引用不影响对象的释放和废弃,若某对象被废弃,则此弱引用将自动失效且处于nil

- (void)viewDidLoad {[super viewDidLoad];id obj = [[Person alloc] init];id __weak obj1 = obj;NSLog(@"---释放Person实例--");[obj release];
}

输出

(lldb) p obj1
(Person *) $0 = 0x0000600001ecc320
2021-12-02 22:54:46.242685+0800 05_内存[47385:318004] ---释放Person实例--
2021-12-02 22:54:48.929859+0800 05_内存[47385:318004] -[Person dealloc]
(lldb) po obj1 nil
(lldb)

循环引用

我们经常使用weak解决循环引用,但是在block内部不允许使用弱指针—>访问成员变量

代码如下

@interface Person : NSObject {@public NSString *_name;
}
@property(nonatomic, copy) void(^block)(void);
@end@implementation Person
- (void)test {__weak typeof(self) weakSelf = self;self.block = ^{// 报错 error  Use of undeclared identifier 'strongSelf'NSLog(@"-------%@", strongSelf->_name);};
}- (void)dealloc {NSLog(@"%s",__func__);
}@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];Person *yang = [[Person alloc] init];[yang test];
}

输出

// 报错
Use of undeclared identifier 'strongSelf'

我们可以加上__strong

@interface Person : NSObject {@public NSString *_name;
}
@property(nonatomic, copy) void(^block)(void);
@end@implementation Person
- (void)test {__weak typeof(self) weakSelf = self;self.block = ^{// ARC下不允许使用弱指针—>访问成员变量  需要加上 __strong__strong typeof(weakSelf) strongSelf = weakSelf;// 加上之后可以正常访问了  其实就是骗编译器通过NSLog(@"-------%@", strongSelf->_name);};
}- (void)dealloc {NSLog(@"%s",__func__);
}@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];Person *yang = [[Person alloc] init];[yang test];
}
@end

Clang

struct __Person__test_block_impl_0 {struct __block_impl impl;struct __Person__test_block_desc_0* Desc;// 内部还是 __weak Person *const __weak weakSelf;__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};

内部还是 __weak

__unsafe_unretained

不安全且不会持有对象,附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象
对比__weak “不会持有对象” 这一特点使它和__weak的作用相似,可以防止循环引用
“不安全“ 这一特点是它和__weak的区别,那么它不安全在哪呢?

__unsafe

代码

// 注意一下代码崩溃 Thread 1: EXC_BAD_ACCESS
- (void)viewDidLoad {[super viewDidLoad];id __weak weakObj = nil;id __unsafe_unretained unsafeUnretainedObj = nil;{id __strong strongObj = [[NSObject alloc] init];weakObj = strongObj;unsafeUnretainedObj = strongObj;NSLog(@"strongObj:%@", strongObj);NSLog(@"weakObj:%@", weakObj);NSLog(@"unsafeUnretainedObj:%@", unsafeUnretainedObj);}// 出作用域NSObject实例释放  释放时遍历对象weak表 把weak类型指针变量设置为nilNSLog(@"-----obj dealloc-----");NSLog(@"weakObj:%@", weakObj);  // 访问nil// Crash NSLog(@"unsafeUnretainedObj:%@", unsafeUnretainedObj);  // 访问不确定空间、释放地址// 如果你能确定NSObject对象一定存在 那么用unsafeUnretainedObj性能更快。不确定时候 使用weak
}

分析

  • 出作用域NSObject实例释放 释放时遍历对象weak表 把weak类型指针变量设置为nil
  • 访问不确定空间、释放地址,出现unsafeUnretainedObj野指针,程序Crash
  • 如果你能确定NSObject对象一定存在 那么用unsafeUnretainedObj性能更快。不确定时候 使用weak

问题

既然 __weak 更安全,那么为什么已经有了 __weak 还要保留 __unsafe_unretained ?

  • __weak仅在ARC中才能使用,而MRC只能使用__unsafe_unretained
  • __weak对性能会有一定的消耗,当一个对象dealloc时,需要遍历对象的weak表,把表里的所有weak指针变量值置为nil,指向对象的weak指针越多,性能消耗就越多。所以__unsafe_unretained比__weak快。当明确知道对象的生命周期时,选择__unsafe_unretained会有一些性能提升。
    比如,MyViewController 持有 MyView,MyView 需要调用 MyViewController 的接口。MyView 中就可以存储__unsafe_unretained MyViewController *_viewController

__autoreleasing

用附有 _autoreleasing修饰符的变量替代 autorelease 方法

二级指针类型的默认修饰符

__autoreleasing 是二级指针类型的默认修饰符


声明一个参数为NSError **的方法,但不指定其所有权修饰符, 调用该方法,发现智能提示中的参数NSError **附有__autoreleasing修饰符

只能修饰自动变量

__autoreleasing修饰符时,必须注意对象变量要为自动变量(局部变量 函数参数),否则编译不通过

属性修饰符

按照属性特质进行区分

原子性

atomic原子性访问
nonatomic非原子性访问

读/写权限

readwrite读写
readonly只读

内存管理

assign “纯量类型”
retain:“拥有关系”(owning relationship)
strong:“拥有关系”(owning relationship)
weak:  “非拥有关系”(nonowning relationship)
copy:  “拷贝”
unsafe_unretained:“不安全非拥有”

方法名

getter=XXX:指定“获取方法”的方法名
setter=XXX:指定“设置方法”的方法名

默认值

引用类型:@property (atomic,readWrite,strong) UIView *view;
基本数据类型:@property (atomic,readWrite,assign) int a;

对照表

assign:__unsafe_unretained

  • 他不能在对象被释放后自动将引用设置为nil 只能用于基本数据类型

retain:__strong

  • retain也会增加引用计数

strong:__strong

  • 可以用在ARC上对属性进行修饰,作为强引用

copy:__strong

  • copy的所有权也是__strong,所以也会进行强引用
  • 区别在于会重新开辟一个空间,指向该引用,新对象引用+1,原来的对象并不会引用+1

weak:__weak

  • 在ARC中使用,作为弱引用。

unsafe_unretained

  • 该引用不对对象保持强引用,并在对象被释放后不会置为nil

问题

用assign修饰“对象类型”(object type)会如何?

会报warning⚠️,当指向对象被释放掉后,再使用该属性会crash。

用strong/weak/copy 修饰“纯量类型”(scalar type)时会如何?

​会报Error❗️,这些修饰符只能用来修饰“对象类型”(object type)。

weak和assign的区别?

assign变量在指向变量释放后不会置为nil,再使用会crash。而weak会置为nil。

weak和strong的区别?

​当一个对象还有strong类型的指针指向时,不会被释放。若仅有weak类型的指针指向时,会被释放。

NSString和NSArray和NSDictionary 用copy还是strong修饰?

都属于“容器类型”(collection)的对象,用copy修饰表示不希望值跟随外部改变,用strong修饰会跟随指向内存地址的内存的改变而改变。
copy的所有权也是__strong,所以也会进行强引用
区别在于会重新开辟一个空间,指向该引用,新对象引用+1,原来的对象并不会引用+1

​NSMutableArray用copy修饰,会怎如何?

变成不可变数组,进行可变操作时会crash​崩溃的原因 看我这篇文章,深浅copy

​xib或storyboard拖的控件为什么是weak?

因为xib或storyboard对该控件已经有一个强引用了,而拖出来的属性应该跟这个控件保持相同的生命周期,所以应该用weak修饰。

autoreleasepool

自动释放池

创建

ARC下只能使用@autoreleasepool,用 @autoreleasepool 块替代 NSAutoreleasePool 类

@autoreleasepool { id __autoreleasing obj = [[NSObject alloc] init];
}

MRC下使用autoreleasepool

@autoreleasepool {id obj = [[NSObject alloc] init];[obj autorelease];
}

MRC下使用NSAutoreleasePool

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];

[pool release] 和 [pool drain]

释放NSAutoreleasePool对象,使用[pool release]与[pool drain]的区别
Objective-C 语言本身是支持 GC 机制的,但有平台局限性,iOS 开发用的是 RC 机制
在 iOS 的 RC 环境下[pool release]和[pool drain]效果一样,但在 GC 环境下drain会触发 GC 而release不做任何操作。使用[pool drain]更佳,一是它的功能对系统兼容性更强,二是这样可以跟普通对象的release区别开。

内存管理(二) ARC相关推荐

  1. iOS内存管理(ARC,MRC)

    iOS内存管理方式: ARC Automatic Reference Counting 自动引用计数 MRC Manual Reference Counting 手动引用计数 更改管理方式: 内存管理 ...

  2. iOS 与OS X多线程和内存管理 笔记 ARC与所有权修饰符

    注:本文为笔记形式,所以很多都是摘抄的.<<iOS 与OS X多线程和内存管理>>书中写的很棒,简单易懂,建议各位看官自己去看看. ####ARC和MRC 前一篇主要是MRC环 ...

  3. Objective-C 内存管理之ARC规则

    基本概念 ARC为自动引用计数,引用计数式内存管理的本质并没有改变,ARC只是自动处理"引用计数"的相关部分. 在编译上,可以设置ARC有效或无效.默认工程中ARC有效,若设置无效 ...

  4. 操作系统概念学习笔记 16 内存管理(二) 段页

    操作系统概念学习笔记 16 内存管理 (二) 分页(paging) 分页(paging)内存管理方案允许进程的物理地址空间可以使非连续的.分页避免了将不同大小的内存块匹配到交换空间上(前面叙述的内存管 ...

  5. Objective-C(9)内存管理之ARC

    ARC机制及判断准则 ARC:Auto Reference Counting 自动引用计数 是一种编译器机制,在编译过程中,为我们的代码添加retain.release.autorelease等方法 ...

  6. Linux任督二脉之内存管理(二) PPT

    五节课的第二节课-内存的动态申请和释放 * slab.kmalloc/kfree./proc/slabinfo和slabtop * 用户空间malloc/free与内核之间的关系 * mallopt ...

  7. Linux内存管理二(页表)

    1.综述 用来将虚拟地址空间映射到物理地址空间的数据结构称为页表,即页表用于建立用户进程的虚拟地址空间和系统物理内存(内存.页帧)之间的关联 实现两个地址空间的关联最容易的方法是使用数组,对虚拟地址空 ...

  8. 高端内存映射之kmap持久内核映射--Linux内存管理(二十)

    日期 内核版本 架构 作者 GitHub CSDN 2016-09-29 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 在内 ...

  9. 内存管理(二) - MRC关键字解读

    本篇主要学习以下几个知识点 alloc/reatin/release/dealloc 理解 autorelease 理解 autorelease GUN 实现 autorelease 苹果 实现 原文 ...

  10. BOOST内存管理(二) --- boost::pool

    Boost库的pool提供了一个内存池分配器,用于管理在一个独立的.大的分配空间里的动态内存分配.Boost库的pool主要适用于快速分配同样大小的内存块,尤其是反复分配和释放同样大小的内存块的情况. ...

最新文章

  1. Linux环境变量设置中配置文件分析(/etc/profile,~/.bashrc等)(转)
  2. 双任务延时原理与空闲任务
  3. linux CMA使用机制分析--基于SigmaStar SSD202
  4. 在Eclipse中使用Java 12
  5. 为什么要给计算机配置IP地址,更改ip地址 为何要重启电脑
  6. 40名大学生被退学,教育部表态:学生对自己不负责,就要付出代价
  7. LAMP或LNMP一键安装包
  8. 软件毕业设计文档流程与UML图之间的关系
  9. 自己实现memcpy/strcpy/strcmp/strcat/strlen/strstr
  10. 支持所有浏览器的右键菜单
  11. 心理学实验必备 | 脑电实验流程及注意事项
  12. 什么是同城商超配送系统
  13. 大学计算机第四讲答案,大学职业生涯规划课第四讲答案
  14. 阿里研究院花几年心得终成趣谈网络协议,附技术官讲解
  15. 终于有人把智慧城市和边缘计算说清楚了
  16. 【转】抽象语法树简介(AST)
  17. python day9
  18. 最全最通俗易懂的设计模式全集
  19. covmatrix matlab,matlab cov函数
  20. 移除Linux体系下不需求的效劳

热门文章

  1. 大专生自学java_大专生自学java如何在3年内学到可以找工作的程度?
  2. 在广联达GTJ2018中怎么画线型预埋铁件?
  3. 一款超级简单的后台管理系统模板
  4. WEB前端程序员找工作跳槽简历该怎么写?
  5. 二条题目:Reading Club | 算法和人生选择:如何最高效地找到合适的那件衣服?...
  6. 剪辑必备神器,视频片段搜索工具!
  7. 查询期刊的ISO版缩写的巧妙方法
  8. 可能与不可能的边界:P/NP问题趣史
  9. 【对讲机的那点事】玩对讲机,数字对讲机DMR制式模块你了解多少?(下)
  10. 矢量图eps在word中的使用的方法