gpt4 book ai didi

ios - 为什么初始化这个 swift 对象会产生 2 而不是 1 的 ARC?

转载 作者:搜寻专家 更新时间:2023-11-01 06:27:14 25 4
gpt4 key购买 nike

我在我的项目中遇到了一个问题,我意识到一个对象没有按需要被释放。我决定测试对象的 ARC,在初始化之后它是 2。在下面这个简单的例子中也是如此。为什么 ARC 是 2 而不是 1?

import SpriteKit

class LevelBuilder:SKNode{
var testNode:SKSpriteNode?
init(with color:SKColor){
super.init()
self.testNode = SKSpriteNode(color: color, size: CGSize(width: 2, height: 2))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

let test = LevelBuilder(with: .red)
print("ARC: \(CFGetRetainCount(test))")

它打印ARC: 2

最佳答案

没有“对象的 ARC”这样的东西。您正在考虑的是保留计数。很难想象还有比保留计数更无意义的数字。它要么为零(在这种情况下对象消失了,因此您永远不会看到它),要么“不为零”。

保留计数是已对对象提出的所有权声明的数量。系统的任何部分都可以随时自由地提出所有权声明。系统的任何部分都可以随时取消其所有权声明。有一个叫做自动释放池的东西,它持有所有权声明,并将“在未来的某个时候”自动释放这些声明。一个对象在任何给定时间都有多个自动释放保留是完全正常的。这将增加保留计数,但保留计数稍后会下降。

如果保留计数在 MRC 下毫无意义(它们的确如此),那么在 ARC 下它们就完全是疯子了,编译器可以在任何时候自由地优化它们,它可以证明它无关紧要,并且经常在它时注入(inject)额外的保留无法证明不需要它们(特别是与函数调用相关)。所以实际值就更没有意义了。例如,在 ARC 下,test 在调用 CFGetRetainCount 之前附加一个额外的 retain 是完全合适的,只是为了确保 test不会发布得太快。

如果你有内存管理问题,你想使用像内存图调试器这样的工具(并且只是寻找强引用和特别是强循环)。检查保留计数只会骗你。

在您的特定情况下,我们可以使用 swiftc -emit-sil 对其进行一些探索,从我们进行字符串插值的那一点开始(即 ""最后一行):

// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%34 = function_ref @$SSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %35
%35 = apply %34(%30, %31, %32, %33) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %37
%36 = alloc_stack $String // users: %39, %37, %41
store %35 to %36 : $*String // id: %37
// function_ref specialized String.init<A>(stringInterpolationSegment:)
%38 = function_ref @$SSS26stringInterpolationSegmentSSx_tcs23CustomStringConvertibleRzs20TextOutputStreamableRzlufCSS_Tg5 : $@convention(method) (@owned String, @thin String.Type) -> @owned String // user: %40
%39 = load %36 : $*String // user: %40
%40 = apply %38(%39, %29) : $@convention(method) (@owned String, @thin String.Type) -> @owned String // user: %42
dealloc_stack %36 : $*String // id: %41
store %40 to %28 : $*String // id: %42
%43 = integer_literal $Builtin.Word, 1 // user: %44
%44 = index_addr %28 : $*String, %43 : $Builtin.Word // user: %58
%45 = metatype $@thin String.Type // user: %56
%46 = load %3 : $*LevelBuilder // users: %48, %47

=========
strong_retain %46 : $LevelBuilder // id: %47
%48 = init_existential_ref %46 : $LevelBuilder : $LevelBuilder, $AnyObject // user: %49
%49 = enum $Optional<AnyObject>, #Optional.some!enumelt.1, %48 : $AnyObject // users: %52, %51
// function_ref CFGetRetainCount
%50 = function_ref @CFGetRetainCount : $@convention(c) (Optional<AnyObject>) -> Int // user: %51
%51 = apply %50(%49) : $@convention(c) (Optional<AnyObject>) -> Int // user: %54
release_value %49 : $Optional<AnyObject> // id: %52
=========

我用 === 行标记了重要部分。 test 有很强的保留。然后将其包装到 AnyObject? 包装器中以传递给 C 函数 (GetRetainCount)。该函数被调用。然后 Optional(即 test)的值被释放。因此,当您调用 GetRetainCount 时,您应该期待一次额外的保留。

但是如果您使用-O 重新编译它,您会注意到没有strong_retain 指令。 ARC 认为额外的保留实际上不是必需的,因此将其删除。所以这表明通过优化保留计数将为 1。我想知道这是不是真的:

$ swiftc main.swift
$ ./main
ARC: 2
$ swiftc -O main.swift
$ ./main
ARC: 1

果然如此。

关于ios - 为什么初始化这个 swift 对象会产生 2 而不是 1 的 ARC?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52599942/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com