Логика переменных по умолчанию для быстрого протокола - PullRequest
0 голосов
/ 11 июня 2018

У меня есть протокол

protocol Example: class {
    var value: Bool { get set }
    func foo()
    func bar()
}

И расширение:

extension Example {

//    var value: Bool { // Error: Extensions must not contain stored properties
//        didSet {
//            switch value {
//            case true:
//                foo()
//            case false:
//                bar()
//            }
//        }
//    }

    func foo() {
        // logic...
    }
    func bar() {
        // logic...
    }
}
  • Когда для value установлено значение true, я хочу, чтобы вызывался foo()
  • Когда value установлен на false, я хочу, чтобы bar() вызывался

Однако я не хочу избыточно внедрять логику didSet{ } в каждом классеэто соответствует Example

Но, если я попытаюсь добавить логику didSet{ } в расширение, Xcode скажет: «Расширения не должны содержать сохраненные свойства».

Что является лучшим методом длядобавить логику наблюдения свойств по умолчанию без необходимости копировать / вставлять в каждый соответствующий класс?

Цель:

Я хочу, чтобы любой подкласс UIView соответствовал моемупротокол Expandable.Требования моего протокола: isExpanded: Bool, expand() и collapse.Я хочу, чтобы isExpanded = true позвонил expand(), а isExpanded = false позвонил collapse() (очень похоже на поведение настройки isHidden).Но для каждого подкласса UIView я не хочу переписывать какую-либо логику.Я хотел бы просто привести класс в соответствие с Expandable и сразу перейти к настройке isExpanded.

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Вам не нужны наблюдатели для того, что вы описываете.Вам просто нужно немного памяти для вашего штата.Поскольку вы знаете, что это NSObject, вы можете сделать это с помощью среды выполнения ObjC.

// Since we know it's a UIView, we can use the ObjC runtime to store stuff on it
private var expandedProperty = 0

// In Xcode 10b1, you can make Expandable require this, but it's probably
// nicer to still allow non-UIViews to conform.
extension Expandable where Self: UIView {
    // We'll need a primitive form to prevent infinite loops. It'd be nice to make this private, but
    // then it's not possible to access it in custom versions of expand()
    var _isExpanded: Bool {
        get {
            // If it's not know, default to expanded
            return objc_getAssociatedObject(self, &expandedProperty) as? Bool ?? true
        }
        set {
            objc_setAssociatedObject(self, &expandedProperty, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
    var isExpanded: Bool {
        get {
            return _isExpanded
        }
        set {
            _isExpanded = newValue
            if newValue { expand() } else { collapse() }
        }
    }
    func expand() {
        _isExpanded = true  // Bypassing the infinite loop
        print("expand")

    }
    func collapse() {
        _isExpanded = false
        print("collapse")
    }
}

Если вы не знали, что это NSObject, вы можете получить то же самое с глобальным (частным)словарь, который отображает ObjectIdentifier -> Bool.Это просто утечка небольшого количества памяти (~ 16 байт на каждый просмотр, который вы свернули).

Тем не менее, я бы так не поступил.Наличие двух способов сделать одно и то же делает все намного сложнее.Я бы либо просто установил isExpanded, либо установил бы isExpanded только для чтения, а expand и collapse.Тогда вам не нужны дополнительные _isExpanded.

0 голосов
/ 11 июня 2018

Вы должны реализовать getter, setter явно:

protocol Example {
   var value: Bool { get set }
   func foo()
   func bar()
}

extension Example {
   var value: Bool {
       get { return value }

       set(newValue) {
           value = newValue
           value ? foo() : bar()
        }
    }


   func foo() {
       print("foo")
   }

   func bar() {
       print("bar")
   }
}
...