Swift内部类可以访问外部类的自我吗?

时间:2014-07-17 23:55:16

标签: swift

我来自Java背景,当你宣布内部类时,它既是静态的,也不能访问外部类的实例,或者它是“#”。不是静态的,可以访问正在操作的外部类的实例。见http://en.wikipedia.org/wiki/Inner_class#Types_of_nested_classes_in_Java

Swift有这个概念吗?从我的测试来看,我似乎无法访问Outer的{​​{1}}对象,但我肯定会做错事。

self

8 个答案:

答案 0 :(得分:34)

AFAIK,您无法开箱即用访问外部类。

但你可以做的是:

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)
    }

}

答案 1 :(得分:3)

嵌套类型无法对包含它们的类型进行任何特殊访问。但是,我不认为这就是他们所做的一切。你似乎对类与实例有点模糊。

value是外类的属性。这意味着Outer的每个实例都有自己的value。 Inner是一个独立的类,存在于Outer的命名空间中。所以当你写let bar = value时,没有value这样的东西可以访问,因为它只存在于Outer的实例中,而我们手边没有任何实例。如果是类属性,则可以let bar = Outer.value

答案 2 :(得分:2)

任何语言的内部类都是一个非常无用的结构。他们没有做任何分离关注的事情。它们只提供了包装类内部的可视化分组,但代码仍然与整个地方的交叉依赖性高度纠缠。

抽象的正确实践是隔离尽可能少的问题(理想情况下,一个)。隔离意味着没有可能的交叉依赖性,这在推理代码时会产生巨大的影响。

问问自己:你真正需要这个外部类的引用是什么? 唯一可能的答案是仅访问其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,它对使用它的控制器一无所知。这意味着我们可以在其他控制器中重用此委托。

您可能一开始并不相信,但事实证明上述方法适用于涉及内部类的所有方案。隔离关注是一般抽象和编程良好实践的基石,因此每当遇到这种问题时,都会重新考虑。

答案 3 :(得分:2)

没有,并且在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

答案 4 :(得分:1)

你可以解决这个问题:

class Outer {
    let value = ""
    class Inner {
        func foo(outer: Outer) {
            let bar = outer.value
        }
    }
}

或者,不是将Outer实例传递给foo方法,而是将其传递给Inner的初始值设定项。但是如果Innerenum,那么这个技巧将不起作用,因为Swift enum不能具有属性(除了关联值,它们就像属性一样)。

这种解决方法相当于Java对内部类隐式执行的操作,以避免给您带来传递参数和显式引用它的繁琐工作。

IMO Java内部类功能的基本原理类似于闭包的基本原理:该功能允许代码隐式使用环境中的变量(在这种情况下,环境是Outer类实例)。我不知道为什么斯威夫特缺乏内部阶级。如果Swift被增强以执行内部类,则必须更改许多现有的Swift代码以将static放在类声明之前。值得,IMO。

答案 5 :(得分:0)

好吧,你可以使用继承,这样你就可以使用外部作为内部的超类。

class Outer {
    let value = ""
    class Inner: Outer {
        func foo() {
            let bar = value //value is now accessable as it has internal access from the superclass
        }
    }
}

答案 6 :(得分:0)

如果您有许多内部类并且想要编写更少的代码行,那么您可以使用初始化程序创建基类,该初始化程序接受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()

答案 7 :(得分:-2)

这将违背面向对象编程的概念。内部类的实例如何知道它所属的外部类的哪个实例?类是蓝图,而不是内存中的实际对象。因此,您可以将外部类的实例的引用传递给内部类中的构造函数/初始化器,以获取从内部实例到外部实例的引用。