swift中嵌套函数的实际用途是啥? [复制]
Posted
技术标签:
【中文标题】swift中嵌套函数的实际用途是啥? [复制]【英文标题】:What is the practical use of nested functions in swift? [duplicate]swift中嵌套函数的实际用途是什么? [复制] 【发布时间】:2016-01-03 06:18:06 【问题描述】:嵌套函数的实际用途是什么?它只会使代码更难阅读,并且不会使特定情况变得容易。
func chooseStepFunction(backwards: Bool) -> (Int) -> Int
func stepForward(input: Int) -> Int return input + 1
func stepBackward(input: Int) -> Int return input - 1
return backwards ? stepBackward : stepForward
Source
【问题讨论】:
你的意思是,与闭包相比? 你检查我的答案了吗? 是的,我期待更多答案。您展示了如何“可以”使用此功能。我问的是如何使用“它”。如果你的项目中有一部分代码是你实际使用的,那对我理解这个概念会更有帮助。谢谢 我宁愿不同意你的观点。看起来不错。根据输入参数将函数返回给调用者已经非常实用了。 我用它们来封装 【参考方案1】:在您的示例中,您可以创建一个variable
,它也是一个function
,如下所示:
var myStepFunction = chooseStepFunction(true)
myStepFunction(4)
嵌套函数的好处非常好。例如,假设您正在为计算器构建一个应用程序,您可以将所有逻辑都放在一个函数中,如下所示:
func doOperation(operation: String) -> ((Double, Double) -> Double)?
func plus(s: Double, d: Double) -> Double
return s + d
func min(s: Double, d: Double) -> Double
return s - d
switch operation
case "+":
return plus
case "-" :
return min
default :
return nil
var myOperationFunction = doOperation("-")?(4, 4) // 0
var myOperationFunction2 = doOperation("+")?(4, 5) //9
在某些情况下,您不允许查看某些功能的实现,或者不对其负责。然后将它们隐藏在其他函数中确实是一个好方法。例如,假设您的同事负责开发 plus
和 min
函数,他/她会这样做,而您只需使用外部函数。
它不同于闭包,因为在 clouser 中,你将你的逻辑传递给其他逻辑,它会调用你的。它是一种插件。例如,调用 http 请求后,您可以传递您希望应用在收到服务器响应时执行的代码
【讨论】:
为什么不直接使用私有函数呢?您的 func("+")?(4,5) 示例非常丑陋且难以阅读。这些都不是很好的实际用途,因为我认为这个任务会以其他方式更好地完成 @Esqarrouth 更好的是,只需使用闭包,或者更好的是,直接使用(+)
或(-)
作为闭包【参考方案2】:
一个用例是递归数据结构上的操作。
例如,考虑以下用于在二叉搜索树中搜索的代码:
func get(_ key: Key) -> Value?
func recursiveGet(cur: Node) -> Value?
if cur.key == key
return cur.val
else if key < cur.key
return cur.left != nil ? recursiveGet(cur: cur.left!) : nil
else
return cur.right != nil ? recursiveGet(cur: cur.right!) : nil
if let root = self.root
return recursiveGet(cur: root)
else
return nil
当然,您可以将递归转换为循环,取消嵌套函数。不过,我发现递归代码通常比迭代变体更清晰。 (权衡与运行时成本!)
您也可以将recursiveGet
定义为get
之外的(私有)成员,但这将是糟糕的设计(除非recursiveGet
用于多种方法)。
【讨论】:
【参考方案3】:有“代码可以是数据”的原则(从Objective-C开始)。您可以传递代码,将其存储在变量中,将其与其他代码组合。这是一个非常强大的工具。坦率地说,如果我没有将代码视为数据的能力,那么我编写的大部分代码都会难写十倍,阅读难十倍。
Swift 中的函数只是闭包,因此嵌套函数非常有意义,因为当您不想使用众多可用快捷方式之一时,这就是您编写闭包的方式。
【讨论】:
【参考方案4】:我认为您问题的核心是:为什么不使用私有函数而不是丑陋的嵌套函数?
简单地说,嵌套函数可以简化可读性和封装。
同样有人会问,(函数的)局部变量与实例变量的实际用途是什么?对我来说,这确实是同一个问题。只是嵌套函数不太常见。
可读性
仍然可以从类中的其他函数访问私有函数。嵌套函数并非如此。您是在告诉您的开发人员,这仅属于此函数(与它们属于整个类的私有函数相反)。退后一步,不要乱用它,如果您需要类似的功能,请自己编写!
当你看到一个私有函数时,你必须思考,哪个函数会调用它。它是第一个函数还是最后一个函数?让我搜索一下。然而,使用嵌套函数,您不必上下查找。已经知道哪个函数会调用它。
此外,如果您有 5 个私有函数,其中 3 个在单个公共函数中调用,那么通过将它们全部嵌套在同一个公共函数下,您就可以与其他开发人员沟通这些私有函数是相关的。
简而言之,就像您不想污染公共命名空间一样,您也不想污染私有命名空间。
封装
另一个方便之处是它可以访问其父函数的所有本地参数。您不再需要传递它们。这最终意味着要测试的函数减少了,因为您已经将一个函数包装在另一个函数中。此外,如果您在非嵌套函数块中调用该函数,则不必将其包装到 self
或考虑创建泄漏。这是因为嵌套函数的生命周期与其包含函数的生命周期相关联。
另一个用例是当你的类中有非常相似的函数时,比如说你有:
extractAllHebrewNames() // right to left language
extractAllAmericanNames() // left to right language
extractAllJapaneseNames() // top to bottom language
现在,如果您有一个名为 printName 的私有函数(用于打印名称),如果您根据语言切换大小写,它会起作用,但如果您不/不能这样做怎么办。相反,您可以在每个单独的提取函数中编写自己的嵌套函数(它们现在都可以具有完全相同的名称。因为每个函数都位于不同的命名空间中。)并打印名称。
你的问题有点类似,为什么不使用“if else”而不是“switch case”。
我认为这只是提供了一种便利(对于可以在不使用嵌套函数的情况下处理的事情)。
注意:嵌套函数应该在函数中的调用点之前编写。
错误:在声明之前使用局部变量“嵌套”
func doSomething()
nested()
func nested()
没有错误:
func doSomething()
func nested()
nested()
类似地,嵌套函数中使用的局部变量必须在嵌套函数之前声明
注意:
如果您使用嵌套函数,则编译器不会强制执行 self
检查。
编译器不够聪明。它不会抛出以下错误:
Call to method 'doZ' in closure requires explicit 'self.' to make capture semantics explicit
这可能导致难以发现内存泄漏。例如
class P
var name: String
init(name: String)
print("p was allocated")
self.name = name
func weaklyNested()
weak var _self = self
func doX()
print("nested:", _self?.name as Any)
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
doX()
func stronglyNested()
func doZ()
print("nested:", name)
DispatchQueue.main.asyncAfter(deadline: .now() + 2)
doZ() // will NOT throw error of: `Call to method 'doZ' in closure requires explicit 'self.' to make capture semantics explicit`
deinit
print("class P was deinitialized")
class H
var p: P
init(p: P)
self.p = p
var h1: H? = H(p: P(name: "john"))
h1?.p.weaklyNested()
h1 = nil // will deallocate immediately, then print nil after 2 seconds
var h2: H? = H(p: P(name: "john"))
h2?.p.stronglyNested()
h2 = nil // will NOT deallocate immediately, will print "john" after 2 seconds, then deallocates
tl;dr 解决方案:
使用weak var _self = self
并在嵌套函数引用中使用self
和weak
引用。
根本不要使用嵌套函数 :( 或者不要在嵌套函数中使用实例变量。
这部分是感谢Is self captured within a nested function?的原帖写的
【讨论】:
以上是关于swift中嵌套函数的实际用途是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
Java内部类的用途是啥?嵌套类和内部类是一样的吗? [复制]