Подтвердить что ты не робот

Имеют ли внутренние классы Swift доступ к внешнему классу?

Я исхожу из фона 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'
        }
    }
}
4b9b3361

Ответ 1

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

}

Ответ 2

Вложенные типы не имеют специального доступа к типам, которые их содержат. Но я не думаю, что все это происходит здесь. Кажется, вы немного расплывчаты в классах против экземпляров.

value является свойством класса Outer. Это означает, что каждый экземпляр Outer имеет свой собственный value. Внутренний - это отдельный класс, который существует в пространстве имен Outer. Поэтому, когда вы пишете let bar = value, для доступа нет такой функции, как value, потому что это существует только в случаях Outer, и у нас нет никаких экземпляров. Если это свойство класса, вы можете сделать let bar = Outer.value.

Ответ 3

Внутренние классы на любом языке - довольно бесполезная конструкция. Они ничего не делают для разделения проблем. Они только обеспечивают визуальную группировку внутренних компонентов класса-обертки, но код по-прежнему сильно запутался в кросс-зависимостях.

Правильная практика абстракции состоит в том, чтобы изолировать куски, которые имеют как минимум набор проблем (в идеале один). Изоляция подразумевает, что кросс-зависимости невозможны, что делает огромную сделку, когда дело доходит до рассуждений о коде.

Спросите себя: зачем вам эта ссылка на внешний класс? Единственный возможный ответ - просто получить доступ к определенной части своего 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, который ничего не знает о контроллере, который его использует. Это означает, что мы можем повторно использовать этот делегат в других контроллерах.

Сначала вы можете не поверить, но на самом деле получается, что вышеупомянутый подход применяется к сценариям all, включающим внутренние классы. Изоляция проблем является краеугольным камнем хорошей практики абстракции и программирования в целом, поэтому всякий раз, когда вы сталкиваетесь с такой проблемой, пересматривайте.

Ответ 4

Нет и иметь подобную функцию в 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

Ответ 5

Вы можете решить эту проблему следующим образом:

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

Или вместо передачи экземпляра Outer в метод foo вы можете передать его инициализатору для Inner. Но этот трюк не сработает, если Inner был enum, потому что Swift enum не может иметь свойства (кроме ассоциированных значений, которые похожи на свойства).

Это обходное решение эквивалентно тому, что Java делает неявным для внутреннего класса, чтобы не обременять вас трудностью передачи аргумента и явным образом ссылаясь на него.

IMO Обоснование функции внутреннего класса Java аналогично обоснованию закрытия: эта функция позволяет коду неявно использовать переменные из среды (в этом случае среда является экземпляром класса Outer). Я не знаю, почему у Свифта не хватает внутренних занятий. Множество существующего кода Swift нужно будет изменить, чтобы поставить static перед объявлениями класса, если Swift будет расширен для внутренних классов. Стоит, ИМО.

Ответ 6

Ну, вы можете использовать наследование, чтобы использовать внешний как суперкласс внутреннего.

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

Ответ 7

Если у вас много внутренних классов и вы хотите писать меньше строк кода, вы можете создать базовый класс с инициализатором, который принимает экземпляр 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()

Ответ 8

Это было бы против концепции программирования OO. Как мог бы экземпляр внутреннего класса узнать, к какому экземпляру внешнего класса он принадлежит? Классы - это синие отпечатки, а не фактические объекты в памяти. Поэтому вы можете передать ссылку экземпляра внешнего класса на конструктор/инициализатор во внутреннем классе, чтобы получить ссылку из внутреннего экземпляра во внешний экземпляр.