如何在 Swift 中演示僵尸对象?
How can I demonstrate a zombie object in Swift?
我读过 How to demonstrate memory leak and zombie objects in Xcode Instruments? 但那是 objective-c。这些步骤不适用。
通过阅读 here 我了解到僵尸是以下对象:
- 解除分配
- 但某些指针仍在尝试指向它们并向它们发送消息。
不确定这与访问已解除分配的对象有何不同。
我的意思是在 Swift 你可以做:
var person : Person? = Person(name: "John")
person = nil
print(person!.name)
人被解除分配了吗?是的!
我们要指向它吗?是的!
有人可以分享导致创建悬挂指针的最常见错误吗?
这不是悬空指针或僵尸。当您使用 !
时,您说的是 "if this is nil, then crash." 您不应该将 person
视为 Swift 中的指针。这是一个价值。该值可能是 .some(T)
,也可能是 .none
(也称为 nil
)。这些都不是悬空的。它们只是两个不同的显式值。 Swift 的 nil
与其他语言中的空指针完全不同。它只会在您明确要求时像空指针一样崩溃。
要创建僵尸,您需要使用 Unmanaged
之类的东西。这在 Swift.
中极为罕见
这是 15 行以下代码的僵尸攻击:
class Parent { }
class Child {
unowned var parent: Parent // every child needs a parent
init(parent: Parent) {
self.parent = parent
}
}
var parent: Parent? = Parent()
let child = Child(parent: parent!) // let's pretend the forced unwrap didn't happen
parent = nil // let's deallocate this bad parent
print(child.parent) // BOOM!!!, crash
这段代码中发生的事情是 Child
持有对 Parent
的无主引用,一旦 Parent
被释放,它就变得无效。该引用包含指向不再存在的父项 (RIP) 的指针,并且访问它会导致崩溃并显示类似于以下的消息:
Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated2018-10-29 20:18:39.423114+0200 MyApp[35825:611433] Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated
注意该代码无法在 Playground 中运行,为此您需要一个常规应用程序。
Zombie objects 是 Objective-C 对象,已被释放,但仍接收消息。
在Objective-C中,__unsafe_unretained
可用于创建一个指向对象的附加指针,不会增加引用计数。在 Swift 中会是 unowned(unsafe)
.
这是一个独立的例子:
import Foundation
class MyClass: NSObject {
func foo() { print("foo"); }
deinit { print("deinit") }
}
unowned(unsafe) let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = obj1
print("exit scope")
}
obj2.foo()
obj1
是一个指向新创建的对象的指针,obj2
是另一个指向同一个对象的指针,但是没有增加引用计数器。当 do { ... }
块离开时,对象被释放。通过 obj2
向其发送消息会导致 Zombie 错误:
exit scope
deinit
*** -[ZombieTest.MyClass retain]: message sent to deallocated instance 0x1005748d0
如果您使用 Managed
(例如 convert pointers to Swift objects to C void
pointers in order to pass them to C callback functions)并且没有选择正确的 retained/unretained 组合,也会发生这种情况。一个人为的例子是:
let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = Unmanaged.passUnretained(obj1).takeRetainedValue()
print("exit scope")
}
obj2.foo()
Zombie Object
是一个释放的对象,它的行为就像活着一样。有可能是因为 dangling pointer
.
dangling pointer
指向内存中数据不可预测的某个地址
Objective-C 有 unsafe_unretained
[About] property attributes. [Example]
Swift有
unowned(unsafe)
[About]参考
Unmanaged
- non ARC
对象代码 的包装器
unowned(unsafe) let unsafeA: A
func main() {
let a = A() // A(ref count = 1)
unsafeA = a
} // A(ref count = 0), deinit() is called
func additional() {
unsafeA.foo() //<- unexpected
}
我读过 How to demonstrate memory leak and zombie objects in Xcode Instruments? 但那是 objective-c。这些步骤不适用。
通过阅读 here 我了解到僵尸是以下对象:
- 解除分配
- 但某些指针仍在尝试指向它们并向它们发送消息。
不确定这与访问已解除分配的对象有何不同。
我的意思是在 Swift 你可以做:
var person : Person? = Person(name: "John")
person = nil
print(person!.name)
人被解除分配了吗?是的!
我们要指向它吗?是的!
有人可以分享导致创建悬挂指针的最常见错误吗?
这不是悬空指针或僵尸。当您使用 !
时,您说的是 "if this is nil, then crash." 您不应该将 person
视为 Swift 中的指针。这是一个价值。该值可能是 .some(T)
,也可能是 .none
(也称为 nil
)。这些都不是悬空的。它们只是两个不同的显式值。 Swift 的 nil
与其他语言中的空指针完全不同。它只会在您明确要求时像空指针一样崩溃。
要创建僵尸,您需要使用 Unmanaged
之类的东西。这在 Swift.
这是 15 行以下代码的僵尸攻击:
class Parent { }
class Child {
unowned var parent: Parent // every child needs a parent
init(parent: Parent) {
self.parent = parent
}
}
var parent: Parent? = Parent()
let child = Child(parent: parent!) // let's pretend the forced unwrap didn't happen
parent = nil // let's deallocate this bad parent
print(child.parent) // BOOM!!!, crash
这段代码中发生的事情是 Child
持有对 Parent
的无主引用,一旦 Parent
被释放,它就变得无效。该引用包含指向不再存在的父项 (RIP) 的指针,并且访问它会导致崩溃并显示类似于以下的消息:
Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated2018-10-29 20:18:39.423114+0200 MyApp[35825:611433] Fatal error: Attempted to read an unowned reference but object 0x1018362d0 was already deallocated
注意该代码无法在 Playground 中运行,为此您需要一个常规应用程序。
Zombie objects 是 Objective-C 对象,已被释放,但仍接收消息。
在Objective-C中,__unsafe_unretained
可用于创建一个指向对象的附加指针,不会增加引用计数。在 Swift 中会是 unowned(unsafe)
.
这是一个独立的例子:
import Foundation
class MyClass: NSObject {
func foo() { print("foo"); }
deinit { print("deinit") }
}
unowned(unsafe) let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = obj1
print("exit scope")
}
obj2.foo()
obj1
是一个指向新创建的对象的指针,obj2
是另一个指向同一个对象的指针,但是没有增加引用计数器。当 do { ... }
块离开时,对象被释放。通过 obj2
向其发送消息会导致 Zombie 错误:
exit scope
deinit
*** -[ZombieTest.MyClass retain]: message sent to deallocated instance 0x1005748d0
如果您使用 Managed
(例如 convert pointers to Swift objects to C void
pointers in order to pass them to C callback functions)并且没有选择正确的 retained/unretained 组合,也会发生这种情况。一个人为的例子是:
let obj2: MyClass
do {
let obj1 = MyClass()
obj2 = Unmanaged.passUnretained(obj1).takeRetainedValue()
print("exit scope")
}
obj2.foo()
Zombie Object
是一个释放的对象,它的行为就像活着一样。有可能是因为 dangling pointer
.
dangling pointer
指向内存中数据不可预测的某个地址
Objective-C 有
unsafe_unretained
[About] property attributes. [Example]Swift有
unowned(unsafe)
[About]参考Unmanaged
-non ARC
对象代码 的包装器
unowned(unsafe) let unsafeA: A
func main() {
let a = A() // A(ref count = 1)
unsafeA = a
} // A(ref count = 0), deinit() is called
func additional() {
unsafeA.foo() //<- unexpected
}