Как определить переменную, которую можно установить и получить в расширении протокола

Я родом из мира Java. Сейчас программирую на Swift 4.

Я хотел бы реализовать абстрактный класс в Swift, я знаю, что в Swift нет такого понятия абстрактного класса. Но я знаю, что мы могли бы имитировать эту концепцию в Swift, используя протокол. Например, это то, что я пробовал:

// With protocol, I can define functions that concrete class have to implement
protocol ProductProvider {
   func getProductNumber() -> Int
}

// with protocol extension, I can define shared (computed) properties and functions among concrete classes that comply with this protocol
extension ProductProvider {
  var maxProductCount: Int {
        return 10
  }
}

Но теперь я хотел бы иметь общую переменную, которую можно было бы установить и получить («общая» означает, что она будет использоваться совместно с классами, соответствующими этому протоколу):

extension ProductProvider {
  var maxProductCount: Int {
        set(newValue) {
           // set to what if I couldn't define private stored variable in extension to hold the most recent set value ?
        }
        get{
           // how to return the most recent value set?
        }
  }
}

Мой вопрос в комментарии выше кода. Как я могу сделать этот набор и получить переменную в расширении протокола в Swift 4? Если это невозможно, какие возможны обходные пути?


person Leem    schedule 04.10.2018    source источник
comment
Мне кажется, вы просто пытаетесь предоставить частные резервные переменные, и maxProductCount на самом деле не должно быть вычисляемым свойством. В Swift нет необходимости в частных резервных переменных, геттерах и сеттерах.   -  person Dávid Pásztor    schedule 04.10.2018
comment
@DávidPásztor Хорошо, но ... как добиться того, чего я хотел бы достичь?   -  person Leem    schedule 04.10.2018
comment
Есть ли реальная причина, по которой вы хотели бы использовать частные резервные переменные, или это просто потому, что вы привыкли делать это с Java? Потому что, если это последнее, короткий ответ: не ищите обходной путь, поскольку в большинстве случаев не имеет смысла достигать того, чего вы пытаетесь достичь в Swift. Вы также должны знать, что если вы не объявляете свойство в самом определении протокола, а только в расширении, это будет необязательным требованием протокола, поэтому соответствующие классы не должны будут реализовывать это свойство.   -  person Dávid Pásztor    schedule 04.10.2018
comment
@DávidPásztor, хорошо, но .. если забыть о приведенном выше коде, мой вопрос пытается решить реальную проблему, что определенно имеет смысл. Проблема заключается в том, как иметь общую бизнес-логику в суперабстракции, то есть протокол в Swift (в Java это абстрактный класс), чтобы конкретные классы, соответствующие протоколу, могли извлечь выгоду из общей бизнес-логики, а не дублировать логику в каждом из них в тем временем могли реализовать свои собственные версии абстрактных функций, объявленных в протоколе. Не могли бы вы предложить, как добиться этого в Swift?   -  person Leem    schedule 04.10.2018
comment
@Leem То, что вы, вероятно, ищете, это общий экземпляр Singleton.   -  person Leo Dabus    schedule 04.10.2018
comment
Вам нужно реализовать поведение, подобное классу, для объекта, чтобы ссылаться на что-либо в Swift.   -  person Vanya    schedule 06.10.2018


Ответы (2)


Я думаю, что самый простой способ - определить переменную в протоколе с помощью геттера и сеттера. Затем в вашем объекте соответствия вы должны объявить переменную для соответствия. Пример:

protocol AbstractObject {

    var maxProductCount: Int { get set }
}

struct ConformObject: AbstractObject {

    var maxProductCount: Int
}

Итак, теперь вы можете использовать свою переменную в своих реализациях по умолчанию.

extension AbstractObject {

    mutating func addOne() -> Int {
        self.maxProductCount += 1
        return self.maxProductCount
    }
} 
person Oni_01    schedule 06.10.2018
comment
Пытаюсь понять: зачем return 1, если вы уже увеличили значение maxProductCount? - person koen; 23.09.2019

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

public final class ObjectAssociation<T: AnyObject> {

    private let policy: objc_AssociationPolicy

    /// - Parameter policy: An association policy that will be used when linking objects.
    public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {

        self.policy = policy
    }

    /// Accesses associated object.
    /// - Parameter index: An object whose associated object is to be accessed.
    public subscript(index: AnyObject) -> T? {

        get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
        set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
    }
}

И в вашем расширении

extension SomeType {

    private static let association = ObjectAssociation<NSObject>()

    var simulatedProperty: NSObject? {

        get { return SomeType.association[self] }
        set { SomeType.association[self] = newValue }
    }
}

Невозможно напрямую хранить типы Swift через ассоциацию объектов. Вы можете хранить, например. NSNumber вместо Int.
Источник: https://stackoverflow.com/a/43056053/1811810

person Ehsan Saddique    schedule 04.10.2018