Swift内部类是否可以访问外部类的self ?



我来自Java背景,当你声明内部类时,它要么是静态的,不能访问外部类的实例,要么它不是静态的,可以访问正在操作的外部类的实例。看到http://en.wikipedia.org/wiki/Inner_class Types_of_nested_classes_in_Java

Swift对此有任何概念吗?从我的测试中,我似乎无法访问Outerself对象,但我肯定可能做错了什么。

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的初始化器。但是如果Innerenum,这个技巧就不起作用了,因为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()

最新更新