ARC 有趣的东西 1
2013-02-28 22:59 curer 阅读(647) 评论(0) 编辑 收藏 举报内容主要来自http://www.galloway.me.uk/2012/01/a-look-under-arcs-hood-episode-1/ 里面加入了一点点自己的吐槽和理解
已经有很久很久没有写一些 under the hood 的东西了, 好久没有学习新的技术了, 不说废话了. ARC 是编译器的技术, 不知道为什么, 我对编译器增加额外代码有着非常大的恐惧, 是因为和C++ 相关么? 不知道, anyway ARC 的确帮我这样的码农减少了工作量, 对于一向懒惰的我来说, 我还是更倾向于省事.
在之前写block 的时候, 有些好奇的家伙们问我,是怎么发现编译器增加代码的. 这个其实很简单, 就是看一些文档了,但是总是有些人比较好奇编译器到底做了什么,不想理所当然的接受.
先瞅瞅我们的测试函数 test_arc.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | @interface ClassA : NSObject @interface ClassA : NSObject { NSNumber *_foo; } @property ( nonatomic , retain) NSNumber *foo = _foo; @end @implementation ClassA @synthesize foo; - ( void )changeFooDirect:( NSNumber *)inFoo { _foo = inFoo; } - ( void )changeFooSetter:( NSNumber *)inFoo { self .foo = inFoo; } - ( NSNumber *)newNumber { return [[ NSNumber alloc] initWithInt:10]; } - ( NSNumber *)getNumber { return [[ NSNumber alloc] initWithInt:10]; } @end |
Mac 环境比较让人dt, 安装Xcode 后 安装Command Line Tools 否则, 会遇到各种各样的问题. 首先关闭ARC
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk/ -arch armv7 -fno-objc-arc -O3 -S -o test_arc.s test_arc.m
这个是开启ARC的命令
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk/ -arch armv7 -fobjc-arc -O3 -S -o test_arc_on.s test_arc.m
让我们先看一下 changeFooDirect 在没有开启arc时的样子, 当然, 如果有人和我一起工作,敢写出这样的代码, 肯定会让我喷的体无完肤的: )
.align 2 .code 16 @ @ "\01-[ClassA changeFooDirect:]" .thumb_func "-[ClassA changeFooDirect:]" "-[ClassA changeFooDirect:]" : @ BB#0: movw r1, :lower16:(_OBJC_IVAR_$_ClassA.foo-(LPC0_0+4)) movt r1, :upper16:(_OBJC_IVAR_$_ClassA.foo-(LPC0_0+4)) LPC0_0: add r1, pc ldr r1, [r1] str r2, [r0, r1] bx lr |
这里可以看出, 只是简单的覆盖变量, 没有retain release, 非常容易产生内存问题. 再看一下 changeFooDirect 开启ARC后的样子
.align 2 .code 16 @ @ "\01-[ClassA changeFooDirect:]" .thumb_func "-[ClassA changeFooDirect:]" "-[ClassA changeFooDirect:]" : @ BB#0: push {r4, r7, lr} mov r4, r0 mov r0, r2 add r7, sp, #4 blx _objc_retain movw r1, :lower16:(_OBJC_IVAR_$_ClassA._foo-(LPC0_0+4)) movt r1, :upper16:(_OBJC_IVAR_$_ClassA._foo-(LPC0_0+4)) LPC0_0: add r1, pc ldr r2, [r1] //r2 = 变量foo在类实例中的偏移量 ldr r1, [r4, r2] //r4 = self 这句的意思就是 r1 = foo; str r0, [r4, r2] //r0 表示参数inFoo, 这里相当于_foo = inFoo; mov r0, r1 pop.w {r4, r7, lr} b.w _objc_release //release ro的变量 也就是 _foo |
这里我们可以看出, ARC retain 新变量, 然后release 旧的变量, 而这个正是我们想要的结果.这段坑爹的代码在ARC下是可以正确运行的
这里我再次声明一下, 类似changeFooDirect 这样的代码 只是用于研究, 访问类实例成员变量时都应该使用property方法而不是直接访问或是修改
changeFooSetter 这里我们看到无论是否开启ARC 生成的代码都是一样的.调用自动生成的setter 方法
.align 2 .code 16 @ @ "\01-[ClassA changeFooSetter:]" .thumb_func "-[ClassA changeFooSetter:]" "-[ClassA changeFooSetter:]" : @ BB#0: movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC1_0+4)) LPC1_0: add r1, pc ldr r1, [r1] b.w _objc_msgSend |
然后我们再看看getNumber 函数 开启ARC后的代码
.align 2 .code 16 @ @ "\01-[ClassA getNumber]" .thumb_func "-[ClassA getNumber]" "-[ClassA getNumber]" : @ BB#0: push {r7, lr} movw r0, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC3_0+4)) mov r7, sp movt r0, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC3_0+4)) movw r2, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC3_1+4)) movt r2, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC3_1+4)) LPC3_0: add r0, pc LPC3_1: add r2, pc ldr r1, [r0] ldr r0, [r2] blx _objc_msgSend movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC3_2+4)) movs r2, #10 movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC3_2+4)) LPC3_2: add r1, pc ldr r1, [r1] blx _objc_msgSend pop.w {r7, lr} b.w _objc_autoreleaseReturnValue //如果没有开启ARC 则没有 _objc_autoreleaseReturnValue |
关于_objc_autoreleaseReturnValue 也有很多有趣的事情, 如果把这个家伙简单的理解成autorelease就大错特错了, 编译器会对这些做优化, 减少一些无谓的retain release 来提高代码效率
newNumber 和我们想的也一样, 在开启ARC的时候, 并没有增加 类似autorelease 的函数, 而是将retain count 保持为1.
如果是普通的临时变量又是什么样子呢?
- ( void )test { NSNumber *test = [[NSNumber alloc] initWithInt:1]; NSLog(@ "%@" , test); } |
.align 2 .code 16 @ @ "\01-[ClassA test]" .thumb_func "-[ClassA test]" "-[ClassA test]" : @ BB#0: push {r4, r7, lr} movw r0, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC4_0+4)) add r7, sp, #4 movt r0, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC4_0+4)) movw r2, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC4_1+4)) movt r2, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC4_1+4)) LPC4_0: add r0, pc LPC4_1: add r2, pc ldr r1, [r0] ldr r0, [r2] blx _objc_msgSend movw r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC4_2+4)) movs r2, #1 movt r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC4_2+4)) LPC4_2: add r1, pc ldr r1, [r1] blx _objc_msgSend mov r4, r0 movw r0, :lower16:(L__unnamed_cfstring_-(LPC4_3+4)) movt r0, :upper16:(L__unnamed_cfstring_-(LPC4_3+4)) mov r1, r4 LPC4_3: add r0, pc blx _NSLog mov r0, r4 pop.w {r4, r7, lr} b.w _objc_release // 这里我们看出release 掉了 test 变量 也和我们想想的一样.ARC 这里对带类成员变量和普通的临时变量是不同的 |
寄存器变量在第一个例子中已经列举了一点, 这个就不赘述啦.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?