我来自Java背景,当你声明内部类时,它要么是静态的,不能访问外部类的实例,要么它不是静态的,可以访问正在操作的外部类的实例。看到http://en.wikipedia.org/wiki/Inner_class Types_of_nested_classes_in_Java
Swift对此有任何概念吗?从我的测试中,我似乎无法访问Outer
的self
对象,但我肯定可能做错了什么。
class Outer {
let value = ""
class Inner {
func foo() {
let bar = value // 'Outer.Type' does not have a member named 'value'
}
}
}
当然,您无法开箱即用地访问外部类。
但是你能做的是:
class Outer {
let value = ""
var inner = Inner()
class Inner {
weak var parent: Outer! = nil
func foo() {
let bar = parent.value
}
}
init() {
inner.parent = self
}
}
或:
class Outer {
class Inner {
unowned let parent: Outer
init(parent: Outer) {
self.parent = parent
}
}
let value = ""
var inner: Inner! = nil
init() {
inner = Inner(parent: self)
}
}
嵌套类型对包含它们的类型没有任何特殊访问权限。但我不认为这就是全部。你似乎对类和实例有点模糊。
value
是Outer类的一个属性。这意味着Outer的每个实例都有自己的value
。Inner是一个独立的类,存在于Outer的命名空间中。所以当你写let bar = value
时,没有value
这样的东西要访问,因为它只存在于Outer的实例中,而我们手头没有任何实例。如果它是一个类属性,您可以使用let bar = Outer.value
。
没有,要在Swift中拥有类似的功能,你需要硬编码。
class Outer {
var value = 0
init(_ value: Int) {
self.value = value
}
func inner() -> Inner {
return Inner(self)
}
class Inner {
let outer: Outer
var value = 1
init(_ outer: Outer) {
self.outer = outer
}
func doSomething() {
print("Outer ", outer.value, " Inner ", value)
}
}
}
let i1 = Outer(1).inner()
i1.doSomething() // Outer 1 Inner 1
let i2 = Outer(2).inner()
i2.doSomething() // Outer 2 Inner 1
你可以这样解决这个问题:
class Outer {
let value = ""
class Inner {
func foo(outer: Outer) {
let bar = outer.value
}
}
}
或者,您可以将Outer
实例传递给foo
方法,而不是将其传递给Inner
的初始化器。但是如果Inner
是enum
,这个技巧就不起作用了,因为Swift的enum
不能有属性(除了关联值,就像属性一样)。
这个解决方法相当于Java隐式地为内部类所做的工作,以避免传递参数并显式地通过它进行引用的繁重工作。
在我看来,Java内部类特性的基本原理类似于闭包的基本原理:该特性允许代码隐式地使用来自环境的变量(在本例中,环境是Outer
类实例)。我不知道为什么Swift缺少内部类。如果Swift被增强为内部类,那么许多现有的Swift代码将不得不被修改,以将static
放在类声明的前面。
内部类在任何语言中都是非常无用的结构。它们对分离关注点毫无帮助。它们只提供包装器类内部的可视化分组,但是代码仍然与交叉依赖高度纠缠在一起。
适当的抽象实践是将处理尽可能少的一组问题的部分分离出来(理想情况下是一个)。隔离意味着不可能存在交叉依赖,这在对代码进行推理时非常重要。
问问你自己:你到底需要这个外部类的引用做什么?唯一可能的答案是仅仅访问其API的某些特定部分。事实证明,这就是将内部类专门化到特定的外部类的原因。
考虑下面的例子:
class CustomSectionContentController : UIViewController {
@IBOutlet weak var webView: UIWebView!
lazy var webViewDelegate: WebViewDelegate = WebViewDelegate()
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = webViewDelegate
}
class WebViewDelegate: NSObject, UIWebViewDelegate {
var overlay: ProgressOverlay?
func webViewDidStartLoad(webView: UIWebView) {
// Here I want to access the 'view' of the outer class:
overlay = ProgressOverlay.cover(outerSelf.view)
}
func webViewDidFinishLoad(webView: UIWebView) {
overlay?.remove()
}
}
}
当然,您可以使用其他答案中建议的多种方法中的任何一种来解决缺失的outerSelf
功能,但这个示例实际上表明,存在一段易于区分的代码,可以将其隔离。考虑以下替代方案:
class CustomSectionContentController : UIViewController {
@IBOutlet weak var webView: UIWebView!
lazy var webViewDelegate: ProgressOverlayWebViewDelegate =
ProgressOverlayWebViewDelegate(view: self.view)
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = webViewDelegate
}
}
class ProgressOverlayWebViewDelegate: NSObject, UIWebViewDelegate {
let view: UIView
init(view: UIView) {
self.view = view
}
var overlay: ProgressOverlay?
func webViewDidStartLoad(webView: UIWebView) {
overlay = ProgressOverlay.cover(view)
}
func webViewDidFinishLoad(webView: UIWebView) {
overlay?.remove()
}
}
看到区别了吗?我们的控制器现在根本不处理WebView委托的问题区域。我们现在还有一个孤立的通用ProgressOverlayWebViewDelegate
,它对使用它的控制器一无所知。这意味着我们可以在其他控制器中重用这个委托。
一开始您可能不相信,但实际上上面的方法适用于所有涉及内部类的场景。一般来说,隔离关注点是抽象和编程的良好实践的基石,所以无论何时遇到这类问题,都要重新考虑。
你可以使用继承,这样你就可以使用outer作为inner的超类。
class Outer {
let value = ""
class Inner: Outer {
func foo() {
let bar = value //value is now accessable as it has internal access from the superclass
}
}
}
如果你有很多内部类,想要写更少的代码行,那么你可以创建一个接受Outer
实例的初始化基类,并从它继承所有的内部类:
class Outer {
func testInner() {
print("test")
}
class InnerBase {
let outer: Outer
init(outer: Outer) {
self.outer = outer
}
}
class Inner1: InnerBase {
func test() {
outer.testInner()
}
}
class Inner2: InnerBase {
func test() {
outer.testInner()
}
}
func test() {
Inner1(outer: self).test()
Inner2(outer: self).test()
}
}
Outer().test()