Быстрое «открытое» ключевое слово и переопределяемый метод/свойства в расширении?

С введением ключевого слова open в Swift 3.0 (Что такое ключевое слово "open" в Swift ?).

Примечание. Ограничено расширениями NSObject производных классов или @objc методов/свойств с атрибутами.

Код, который объявлял и использовал public (class) методов/свойств в расширении между модулями/фреймворками, сломался, поскольку public больше не означает "переопределяемый" за пределами определяющего модуля.

Пример:

public extension UIManagedDocument {

    public class func primaryDocumentName() -> String {
        return "Document"
    }

    public class func primaryStoreURL() -> URL {
        let documentsURL = FileManager.default.userDocumentsURL
        return URL(fileURLWithPath: self.primaryDocumentName(), isDirectory: false, relativeTo: documentsURL)
    }

    public class func primaryModelName() -> String? {
        return "Model"
    }

}
  • Исходное предложение (SE -0117) ориентирован на создание подклассов и не упоминает расширения.
  • В настоящее время расширения не поддерживают ключевое слово open (вы не можете писать open extension NSObject так же, как open func Method())

Вопрос. Есть ли способ переопределить методы/свойства, предоставляемые расширением, в модулях/фреймворках?


person Nocross    schedule 25.08.2016    source источник
comment
Это действительно связано с новыми режимами открытого и публичного доступа? Если я не ошибаюсь, вы все равно не можете переопределить методы, объявленные в расширениях, ни в Swift 2, ни в Swift 3).   -  person Martin R    schedule 25.08.2016
comment
Вы правы для чистых быстрых классов, но можно использовать классы, производные от NSObject, а также с @objc атрибутированными методами/свойствами. (Можете ли вы переопределить между расширениями в Swift или нет?)   -  person Nocross    schedule 25.08.2016
comment
Ясно спасибо. (Возможно, вы можете добавить эту информацию к вопросу).   -  person Martin R    schedule 25.08.2016
comment
И спасибо, что напомнили мне о моем собственном ответе :)   -  person Martin R    schedule 25.08.2016


Ответы (2)


Если я не ошибаюсь, вы можете объявить методы расширения как open в своей структуре, если просто опустите ключевое слово public в объявлении расширения:

extension UIManagedDocument {

    open class func primaryDocumentName() -> String {
        return "Document"
    }
    // ...
}

И затем (для NSObject подклассов или @objc участников) вы можете переопределить метод в своем пользовательском подклассе в основном приложении (или в любом модуле):

class MyManagedDocument: UIManagedDocument {

    override class func primaryDocumentName() -> String {
        return "MyDocument"
    }
    // ...
}
person Martin R    schedule 25.08.2016
comment
Я не могу опустить public слово, так как расширение объявлено в другом модуле (общая структура), а конкретный подкласс реализован в зависимой цели (приложение/расширение). - person Nocross; 25.08.2016
comment
@Nocross: На самом деле я только что проверил это, и у меня это сработало. extension UIManagedDocument {} находится во фреймворке, а class MyManagedDocument: UIManagedDocument {} — в основном приложении. - person Martin R; 25.08.2016
comment
Вы правы, это работает. Но что меня беспокоит, так это то, что таким образом мы полагаемся на быструю логику «вывода», которая, по крайней мере, нестабильна, а также может быть специфичной для разных IDE. - person Nocross; 25.08.2016
comment
@Nocross: я не думаю, что это связано с IDE. Обратите внимание, что вы также удалили ключевое слово public в своем решении. - person Martin R; 25.08.2016
comment
Нюанс немного в другом. Я опустил его, так как расширение соответствия протоколу также соответствует модификаторам доступа из протокола. - person Nocross; 25.08.2016
comment
@Nocross: насколько я понимаю, уровень доступа расширения по умолчанию — это уровень доступа самого класса (в данном случае: открытый). - person Martin R; 25.08.2016

  • «Ориентированный на протокол» — объявите протокол с нужными методами/свойствами, а затем реорганизуйте расширение для соответствия протоколу.
  • «Традиционный» — реализовать промежуточный (абстрактный) подкласс с желаемыми методами/свойствами.

Пример протокола:

protocol PrimaryDocument {
    static func primaryDocumentName() -> String

    static func primaryStoreURL() -> URL

    static func primaryModelName() -> String?
}

extension UIManagedDocument : PrimaryDocument {

    open class func primaryDocumentName() -> String {
        return "Document"
    }

    open class func primaryStoreURL() -> URL {
        let documentsURL = FileManager.default.userDocumentsURL
        return URL(fileURLWithPath: self.primaryDocumentName(), isDirectory: false, relativeTo: documentsURL)
    }

    open class func primaryModelName() -> String? {
        return "Model"
    }

}
person Nocross    schedule 25.08.2016