为什么初始化此 swift 对象会产生 2 而不是 1 的 ARC?
Why does initializing this swift object produce an ARC of 2 instead of 1?
我 运行 在我的项目中遇到了问题,我意识到没有按需要释放对象。我决定测试对象的 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
可能是因为您初始化了 testNode,它是一个 SKSpriteNode,它也可能在底层引用 SKNode。因此,您的第一个引用来自 LevelBuilder class,第二个引用来自 testNode。
不存在 "the ARC of the object." 你考虑的是保留计数。很难想象还有比保留计数更无意义的数字。它要么是零(在这种情况下对象消失了,所以你永远不会看到它),要么是 "not zero."
保留计数是对对象提出的所有权声明的数量。系统的任何部分都可以随时自由地提出所有权声明。系统的任何部分都可以随时取消其所有权声明。有一个叫做自动释放池的东西,它持有所有权声明并会自动释放这些声明 "at some point in the future." 一个对象在任何给定时间拥有多个自动释放保留是完全正常的。这将增加保留计数,但保留计数稍后会下降。
如果保留计数在 MRC 下毫无意义(它们确实如此),那么它们在 ARC 下就完全是疯子了,编译器可以随时自由地优化它们,它可以证明这无关紧要,并且经常注入额外的保留当它不能证明不需要它们时(特别是与函数调用相关)。所以实际值就更没有意义了。例如,在 ARC 下,test
在调用 CFGetRetainCount
之前附加一个额外的保留是完全合适的,只是为了确保 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
果然.
我 运行 在我的项目中遇到了问题,我意识到没有按需要释放对象。我决定测试对象的 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
可能是因为您初始化了 testNode,它是一个 SKSpriteNode,它也可能在底层引用 SKNode。因此,您的第一个引用来自 LevelBuilder class,第二个引用来自 testNode。
不存在 "the ARC of the object." 你考虑的是保留计数。很难想象还有比保留计数更无意义的数字。它要么是零(在这种情况下对象消失了,所以你永远不会看到它),要么是 "not zero."
保留计数是对对象提出的所有权声明的数量。系统的任何部分都可以随时自由地提出所有权声明。系统的任何部分都可以随时取消其所有权声明。有一个叫做自动释放池的东西,它持有所有权声明并会自动释放这些声明 "at some point in the future." 一个对象在任何给定时间拥有多个自动释放保留是完全正常的。这将增加保留计数,但保留计数稍后会下降。
如果保留计数在 MRC 下毫无意义(它们确实如此),那么它们在 ARC 下就完全是疯子了,编译器可以随时自由地优化它们,它可以证明这无关紧要,并且经常注入额外的保留当它不能证明不需要它们时(特别是与函数调用相关)。所以实际值就更没有意义了。例如,在 ARC 下,test
在调用 CFGetRetainCount
之前附加一个额外的保留是完全合适的,只是为了确保 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
果然.