Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift关闭:捕获列表必须详尽吗?_Swift_Memory_Memory Management_Closures - Fatal编程技术网

Swift关闭:捕获列表必须详尽吗?

Swift关闭:捕获列表必须详尽吗?,swift,memory,memory-management,closures,Swift,Memory,Memory Management,Closures,假设我有一个这样的Swift类: @objc final MyClass : NSObject { let classPropertyString = "A class property" func doStuff() { let localString = "An object local to this function" DispatchQueue.global(qos: .userInitiated).async { [cl

假设我有一个这样的Swift类:

@objc final MyClass : NSObject
{
    let classPropertyString = "A class property"


    func doStuff() 
    {
        let localString = "An object local to this function"

        DispatchQueue.global(qos: .userInitiated).async { [classPropertyString] in
             // Do things with 'classPropertyString' and 'localString'
        }
    }
}
我的问题是:当我写一个捕获列表时,我是否有责任详尽地列出所有我希望闭包包含强引用的东西


换句话说,如果我从捕获列表中省略了
localString
(就像我在这里所做的那样),那么闭包是否仍然会自动捕获对它的强烈引用,或者我是否处于一个糟糕的时期?

您的问题有一些小的怪癖,使得很难清楚地回答,但我想我理解潜在的问题,简短的回答是“不可能”。但是你的例子是不可能的,所以答案是“不可能”。如果可能的话,就没有强有力的参考(也没有必要),所以问题仍然是“不可能”。即使如此,我们还是来看看这里发生了什么

首先,
closure
不能引用
localString
,除非在
doStuff()
内的注释中以某种方式重新分配它<代码>闭包在
localString
不在范围内的级别分配。闭包只能在赋值时捕获范围内的变量,而不能在调用时捕获范围内的变量。但是让我们回到这个问题的原始版本,在它被编辑之前。该版本确实有您描述的案例:

@objc final myClass : NSObject
{
    let classPropertyString = "A class property"


    func doStuff() 
    {
        let localString = "An object local to this function"

        DispatchQueue.global(qos: .userInitiated).async { [classPropertyString] in // (1)
             // Do things with 'classPropertyString' and 'localString'
        }
        // (2)
    }
}
这里没有问题
classPropertyString
被复制到闭包中,避免任何保留循环<代码>本地字符串被闭包引用,因此只要闭包存在,它就会被保留

因为您在捕获列表中列出了
classPropertyString
,所以会在点(1)处对其求值并复制到闭包中。因为您隐式捕获了
localString
,所以它被视为引用。请参阅Swift编程语言参考中的一些优秀示例,这些示例确切地说明了这在不同情况下是如何工作的

在任何情况下(*)都不会允许您在闭包中使用的东西的底层存储在背后消失。这就是为什么典型的问题是过多的保留(内存泄漏),而不是挂起引用(崩溃)

(*)“无论如何”这是一个谎言。斯威夫特有几种方法可以做到这一点,但几乎所有的方法都涉及“不安全”,这是你对此的警告。主要的例外是
无主
,当然还有任何涉及
的东西类型。而且Swift通常不是线程安全的,所以你需要小心

关于线程安全性的最后一点评论是,隐式和显式捕获之间的细微差别确实很重要。考虑在两个队列中修改隐式捕获值的情况:

func doStuff() -> String
{
    var localString = "An object local to this function"

    DispatchQueue.global(qos: .userInitiated).async {
         localString = "something else"
         callFunction(localString)
    }

    localString = "even more changes"
    return localString
}
在这种情况下会发生什么?天哪,别那么做。我相信这是未定义的行为,localString可能是任何东西,包括损坏的内存,至少在最一般的情况下是这样(它可能是调用
.async
;我不确定)的定义行为。但不要这样做

但对于正常情况,没有理由显式捕获局部变量。(有时我希望SWIFT已经走了C++的方式,说它是必需的,但它不是。) 好吧,还有一种方式是隐式和显式的,它们是不同的,这可能会让我们明白它们是如何工作的。考虑像这样的状态关闭(我经常这样做):

查看闭包如何捕获局部变量
n
,并在其超出范围后进行修改。并查看
inc2
如何拥有该局部变量的自己版本。现在尝试显式捕获

func incrementor() -> () -> Int {
    var n = 0
    return { [n] in // <---- add [n]
        n += 1      // Left side of mutating operator isn't mutable: 'n' is an immutable capture
        return n
    }
}
func incrementor()->()->Int{
var n=0

在//中返回{[n],尽管苹果公司在其文档中没有讨论“定义捕获列表中应强烈捕获的项目”方法(),我在不同的地方看到过它,我想知道它的语义。因此,这个问题。我RTFM.与您的问题无关,但它是以大写字母开始命名所有类的快速命名约定。嗯…问题:在第一个示例中,指定[classPropertyString]在捕获列表中,它实际上会复制值,因为它是实例化闭包的时间点。但是,据我所知,不会复制localString,而是通过引用语义绑定到闭包,因为它没有指定为显式捕获。这与它是值类型这一事实无关(参见第一个n示例)事实上,它是一个let常量。感谢您进行了非常全面的演练;这真是太棒了。尽管我理解了为什么使用GCD不会创建一个保留周期,但我还是回到了原来的示例。正如您所知,我是一名长期的C/ObjC开发人员,正在向Swift过渡,所以我仍然对细节有自己的看法。@Ham谢谢你的评论。我已经编辑过(大部分只是删除了不准确的句子。)谢谢@MichaelLong.edited。
func incrementor() -> () -> Int {
    var n = 0
    return { [n] in // <---- add [n]
        n += 1      // Left side of mutating operator isn't mutable: 'n' is an immutable capture
        return n
    }
}