someFunction(completion: { [weak self] in
self?.variable = self!.otherVariable
})
总是安全吗?我在语句的开头访问了可选的self
,并且我个人认为如果self
是nil
,则永远不会执行此语句的第二部分。这是真的?如果self
确实是nil
,那么第二部分永远不会发生?并且self
可能永远不会发生'#1}}在这一行代码中?
答案 0 :(得分:25)
Optional Chaining 来自“Swift编程语言” 给出以下示例:
let john = Person() // ... let someAddress = Address() // ... john.residence?.address = someAddress
接着(强调补充):
在此示例中,尝试设置john.residence的地址属性将失败,因为john.residence目前为零。
赋值是可选链接的一部分,这意味着不会评估=运算符右侧的代码。
适用于您的案例:
self?.variable = self!.otherVariable
如果self
为nil
,则右侧的不评估。
因此,你的问题的答案
如果自己确实是零,第二部分永远不会发生?
是“是”。关于第二个问题
在这一行代码中,自我永远不会被“掏空”吗?
我原来的假设是self
一旦被确定为!= nil
,
在整个评估过程中都会对self!
提出强烈的引用
声明,这样就不会发生。然而(正如@Hamish指出的那样),
这不保证。 Apple工程师Joe Groff在Confirming order of operations写道
斯威夫特论坛:
这不保证。版本可以优化为在此之前发生,在最后一次正式使用强引用之后的任何时刻。由于之后没有使用为了评估左侧
绑定的强引用weakProperty?.variable
而加载的强引用,因此没有任何东西可以保持它活着,所以它可以立即释放。
如果getter for getter中有任何副作用导致weakProperty
引用的对象被释放, nil-out弱引用,则会导致右侧的force-unwrap失败。强> 你应该使用if来测试弱引用,并引用if let
答案 1 :(得分:16)
正如@Hamish在下面的评论中指出的那样,Swift编译工程师Joe Groff describes无法保证在RHS的持续时间内保留强大的参考资料。评估[强调我的]
确认操作顺序
<强> Rod_Brown:强>
你好,
我想知道弱变量上某种访问的安全性:
class MyClass { weak var weakProperty: MyWeakObject? func perform() { // Case 1 weakProperty?.variable = weakProperty!.otherVariable // Case 2 weakProperty?.performMethod(weakProperty!) } }
上面两种情况,是由Swift保证的
weakProperty
可以在这些位置强行打包吗?我很好奇Swift在访问过程中提供的保证 可选链接例如是
weakProperty!
访问者保证 如果可选链接首先确定该值,则仅触发if 已经非nil
?此外,保证弱对象保留 评估的持续时间,或弱变量可能是 能够在可选访问和方法之间解除分配 叫?
<强> Joe_Groff:强>
这不是保证。可以优化版本以更早发生 比这个,在最后一次正式使用强者之后的任何一点 参考。 自加载强引用以进行评估 之后不使用左侧
weakProperty?.variable
, 没有什么可以让它保持活力,所以它可以立即生效 释放。如果变量的getter中有任何副作用 导致weakProperty
引用的对象被释放,nil
- 输出弱引用,然后会导致 强行解开右侧失败。你应该使用if let来测试 弱引用,并引用由if约束的强引用 让:if let property = weakProperty { property.variable = property.otherVariable property.performMethod(property) }
这应该更安全,也更有效,因为弱参考是 加载并测试一次而不是四次。
考虑到Joe Groff上面回答的回答,我之前的回答没有实际意义,但我会把它留在这里作为一个可能有趣(虽然失败)的旅程深入Swift编译器。
我根据我对@appzYourLife的评论得出这个答案:删除的答案:
这是纯粹的猜测,但考虑到有点接近 许多经验丰富的Swift核心开发人员与C ++之间的联系 提升lib,我会假设
weak
引用被锁定为 表达式生命周期中强大的一个,如果这个指定/变异self
中的内容,与明确使用的内容非常相似std::weak_ptr::lock()
C ++对应的。
让我们看一下您的示例,其中self
已被weak
引用捕获,而当访问赋值表达式的左侧时,nil
不是self?.variable = self!.otherVariable
/* ^ ^^^^^-- what about this then?
|
\-- we'll assume this is a success */
weak
我们可以在Swift运行时swift/include/swift/Runtime/HeapObject.h
specifically中查看/// Load a value from a weak reference. If the current value is a
/// non-null object that has begun deallocation, returns null;
/// otherwise, retains the object before returning.
///
/// \param ref - never null
/// \return can be null
SWIFT_RUNTIME_EXPORT
HeapObject *swift_weakLoadStrong(WeakReference *ref);
(Swift)引用的基本处理:
weak
这里的关键是评论
如果当前值是已开始取消分配的非null对象, 返回null;否则,在返回前保留对象。
由于这是基于后端运行时代码注释,它仍然有点推测,但我会说上面暗示当试图访问weak
引用指向的值时,确实会引用在通话期间保留为强者(&#34; ......直到返回&#34; )。
为了尝试从上面兑换&#34;有些推测性的&#34; 部分,我们可能会继续深入研究Swift如何通过swift_weakLoadStrong(...)
引用来处理值的访问。从@idmean:s comment below(研究生成的SIL代码,例如OP:s)我们知道调用swift_weakLoadStrong(...)
函数。
因此,我们首先考察swift/stdlib/public/runtime/HeapObject.cpp
中HeapObject *swift::swift_weakLoadStrong(WeakReference *ref) {
return ref->nativeLoadStrong();
}
函数的实现情况,并了解我们从哪里获取:
nativeLoadStrong()
我们从swift/include/swift/Runtime/HeapObject.h
WeakReference
HeapObject *nativeLoadStrong() {
auto bits = nativeValue.load(std::memory_order_relaxed);
return nativeLoadStrongFromBits(bits);
}
方法的实现
nativeLoadStrongFromBits(...)
从the same file开始,HeapObject *nativeLoadStrongFromBits(WeakReferenceBits bits) {
auto side = bits.getNativeOrNull();
return side ? side->tryRetain() : nullptr;
}
的实施:
tryRetain()
继续调用链,HeapObjectSideTableEntry
是HeapObject* tryRetain() {
if (refCounts.tryIncrement())
return object.load(std::memory_order_relaxed);
else
return nullptr;
}
的方法(对the object lifecycle state machine来说很重要),我们在swift/stdlib/public/SwiftShims/RefCount.h
tryIncrement()
可以找到RefCounts
类型的// Increment the reference count, unless the object is deiniting.
bool tryIncrement() {
...
}
方法(此处通过typedef
:ed specialization of it的实例调用)的实现in the same file as above:
lhs
我相信这里的评论足以让我们使用这个方法作为终点:如果对象没有去除(我们已经假定它不是,那么它就不是,作为HeapObject
的赋值假设OP:s示例成功),对象的(强)引用计数将增加,并且weak
指针(由强引用计数增量支持)将传递给赋值运算符。我们不需要研究如何在赋值结束时最终执行相应的引用计数减量,但现在知道超出推测,与{{1}}引用相关联的对象将在生命周期内保留为强对象赋值,因为它在左手边访问时没有被释放/解除分配(在这种情况下,它的右侧将永远不会被处理,如@MartinR:s answer中所述)
答案 2 :(得分:3)
这总是安全吗
没有。你没有做过强弱的舞蹈&#34;。做吧!每当你使用weak self
时,你应该安全地打开Optional,然后只参考那个解包的结果 - 就像这样:
someFunction(completion: { [weak self] in
if let sself = self { // safe unwrap
// now refer only to `sself` here
sself.variable = sself.otherVariable
// ... and so on ...
}
})
答案 3 :(得分:2)
文档明确states,如果确定作业的左侧为零,则不会评估右侧。
但是,在给定的示例中,self
是弱引用,并且可能在可选检查通过后立即释放(并且无效),但是在强制解包之前,将使整个表达式为零-unsafe。
答案 4 :(得分:-4)
在更正之前:
我认为其他人已经尽可能地回答了你问题的细节。
但除了学习。如果您确实希望您的代码可靠地运行,那么最好这样做:
someFunction(completion: { [weak self] in
guard let _ = self else{
print("self was nil. End of discussion")
return
}
print("we now have safely 'captured' a self, no need to worry about this issue")
self?.variable = self!.otherVariable
self!.someOthervariable = self!.otherVariable
}
更正后
感谢MartinR在下面的解释,我学到了很多东西。
阅读这篇伟大的post on closure capturing。每当你在括号[]
中看到某些内容时,我都会盲目地想到它意味着它被捕获了,它的价值并没有改变。但我们在括号中做的唯一事情就是我们weak
- 如果它正在使我们知道它的价值可以成为{{1} }}。如果我们做了类似nil
之类的事情,我们就会成功捕获它,但是我们仍然会遇到一个问题,即持有一个指向[x = self]
本身的强指针并创建一个内存周期。 (从某种意义上来说,这很有意思,因为你因为你的弱点而被释放,因此从创建内存周期到创建崩溃是一条很细的路线。)
总结如下:
self
创建记忆周期。不好!</ p>
[capturedSelf = self]
(如果你事后强行 [weak self]
in guard let _ = self else{
return
}
打开会导致崩溃)self
它是完全无用的。因为下一行,guard let
仍然可以成为self
。不好!</ p>
nil
(如果您强制[weak self]
self?.method1()
解包后可能会导致崩溃。如果self
不是self
,则可能会失败。如果nil
为{{1},则会安全失败这是你最想要的东西。这是好!
self
如果nil
已取消分配,则会安全失败;如果不是[weak self] in
guard let strongSelf = self else{
return
}
,则会继续。但它有点挫败了目的,因为当你删除它自己的引用时,你不应该与self
进行通信。我无法想到一个很好的用例。这可能没用!