【问题标题】:How to create local scopes in Swift?如何在 Swift 中创建本地范围?
【发布时间】:2014-07-23 13:24:50
【问题描述】:

我经常在 Objective-C 中使用本地作用域来使命名更清晰。

{
    UILabel *label = [[UILabel alloc] init];
    [self addSubview:label];
    self.titleLabel = label;
}

我正在尝试像这样在 Swift 中重写这段代码:

{
    let label = UILabel()
    self.addSubview(label)
    self.titleLabel = label
}

这给了我以下错误:

Error: Braced block of statements is an unused closure.

那么如何在 Swift 中创建本地作用域?

【问题讨论】:

  • 尝试使用 'let' 而不是 'var'
  • 你的 Swift 代码中有一个分号 ;)
  • 不再有!肯定需要一些时间来适应这个:)
  • 我通常把它(即范围介绍)作为一个函数做的太多需要拆分的标志。
  • @molbdnilo:我以前也这么认为。直到我遇到了正确拆分的功能。我研究了第一个:它调用了 3 个其他函数。我研究了第一个:它调用了 5 个其他函数。 ....我升了一级,然后继续升到了 2 级……反正我忘记了我在这里做什么。当然没有文件。我更喜欢一个有 100 行代码的大函数,至少当我必须阅读它并且没有文档时。只有在您知道各个函数调用的目的以及它们是否以连贯的方式拆分时,拆分才会有帮助。

标签: swift


【解决方案1】:

更新:在 Swift 2.0 中,您只需使用 do 关键字:

do {
    let label = UILabel()
    self.addSubview(label)
    self.titleLabel = label
}

2.0 之前的 Swift 也是如此:

你可以定义类似这样的东西:

func locally(@noescape work: () -> ()) {
    work()
}

然后使用这样的locally 块如下:

locally {
    let g = 42
    println(g)
}

(灵感来自 Scala 的 Predef 对象中的 locally。)

【讨论】:

  • 这是一个非常酷的解决方案,它为更多块打开了可能性。我可以看到类似于 C# 的 using 的东西,用 Disposable 协议创建。
  • 这看起来是一个很好的解决方案;是否有任何开销,或者编译器是否成功对其进行了优化,使其就像一个好的旧普通块?
  • @Haravikk 我不知道。我会假设那里有 一些开销。
  • @Pang, Jean-Philippe:从 Swift 1.2 开始,您可以添加 @noescape 属性,然后不再需要显式指定 self。比较stackoverflow.com/questions/28427436/…
  • Pre 2.0 你可以使用:do { /* code here */ } while false
【解决方案2】:

Swift 2 开始,您可以使用 do-statement 创建本地范围:

do {
    let x = 7
    print(x)
}
print(x) // error: use of unresolved identifier 'x'

然而,主要用例似乎是使用 do-try-catch 进行错误处理, 如"Error Handling" 中所述 在"The Swift Programming Language",例如:

do {
    let jsonObj = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
    // success, do something with `jsonObj`...

} catch let error as NSError {
    // failure
    print("Invalid JSON data: \(error.localizedDescription)")
}

【讨论】:

    【解决方案3】:

    我认为这是不可能的。

    至少 iBooks 商店中提供的书中的语法没有提及。

    你可以这样做,

    if (true) {
        let a = 4
    }
    

    但我认为,这是一种不好的做法。

    【讨论】:

    • 不太确定这是不好的做法 - 说得太早了,不是吗?这不会生成任何额外的代码,因为 if(true) 肯定会被丢弃。
    • 您断言它使代码的可读性降低是一种观点,而不是事实。对于使用 C 块,您也可以这么说——“{”、缩进代码和尾随“}”也降低了可读性。在我看来不是,但每个人都会有一个。我在我的代码中一直使用#ifdef 0,以及#if var == 1,我发现这非常有用。另一方面,您可能会讨厌它。正如他们所说的那样。
    • 我这么说是因为当你看到 if (expression) {... 那么你期待一些条件代码,如果你看到 if (true) 你可能想知道它是临时更改还是一些 hack 或为什么有人会写这样的代码。当然,如果没有其他人阅读您的代码,也没关系。
    • 你也可以去掉 true 周围的 ( ) 。另一个想法:{ ... }(),尽管目前这会使编译器崩溃。
    【解决方案4】:

    正如 cmets 中所述,C 中的匿名嵌套范围通常表明您可以编写更好的代码。例如,不是简单地在最终设置self.titleLabel 的嵌套范围内工作,您可以 您可以改为将该分配作为评估内联闭包的结果:

    self.titleLabel = {
        let label = UILabel()
        label.text = "some text"
        // ... set other properties ...
        self.addSubview(label)
        return label
    }()
    

    这不仅将label 保留为一个漂亮的短名称,其范围仅适用于创建和配置它的代码块,而且使该代码块与其正在为其创建值的属性相关联。 而且它更加模块化,因为你可以用调用其他一些标签创建函数来替换整个闭包,如果分解该代码变得有用的话。

    如果您发现自己经常做这种事情,您可以尝试制作一个通用函数,让您将构建代码缩减为:

    self.titleLabel = makeSubview(UILabel()) { label in
        label.text = "some text"
        // other label properties
    }
    

    但我将把定义这样的函数作为练习留给读者。 ;)


    Jean-Philippe Pellet's answer 中所述,在 Swift 2.0 及更高版本中,do 构造是语言明确提供的执行此操作的方法。 (他建议的基于函数的解决方案对于仍在使用 Swift 1.x 的任何人来说都是一个不错的选择。)

    另一个 Swift 1.x 解决方案——不定义新函数——是(明确地)丢弃立即执行的闭包的结果:

     _ = {
         print("foo")
     }() 
    

    【讨论】:

    • 我建议通过删除 printlnlet 语句来编辑此答案,因为据我所知它们已被贬值/不必要:)。谢谢你展示这个,我赞成,@rickster
    猜你喜欢
    • 2015-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    相关资源
    最近更新 更多