Поиск…


Свойство в расширении протокола достигается с помощью связанного объекта.

В Swift расширения протокола не могут иметь истинных свойств.

Однако на практике вы можете использовать технику «связанного объекта». Результат почти точно как «реальное» свойство.

Вот точная методика добавления «связанного объекта» к расширению протокола:

По сути, вы используете объектно-c-вызовы objc_getAssociatedObject и _set.

Основные вызовы:

get {
   return objc_getAssociatedObject(self, & _Handle) as! YourType
   }
set {
   objc_setAssociatedObject(self, & _Handle, newValue, .OBJC_ASSOCIATION_RETAIN)
    }

Вот полный пример. Две критические точки:

  1. В протоколе вы должны использовать «: class», чтобы избежать проблемы с мутацией.

  2. В расширении вы должны использовать «where Self: UIViewController» (или любой подходящий класс), чтобы указать подтверждающий тип.

Итак, для примера property "p":

import Foundation
import UIKit
import ObjectiveC          // don't forget this

var _Handle: UInt8 = 42    // it can be any value

protocol Able: class {
    var click:UIView? { get set }
    var x:CGFloat? { get set }
    // note that you >> do not << declare p here
}

extension Able where Self:UIViewController {
       
    var p:YourType { // YourType might be, say, an Enum
      get {
          return objc_getAssociatedObject(self, & _Handle) as! YourType
          // HOWEVER, SEE BELOW
          }
      set {
          objc_setAssociatedObject(self, & _Handle, newValue, .OBJC_ASSOCIATION_RETAIN)
          // often, you'll want to run some sort of "setter" here...
          __setter()
          }
    }
    
    func __setter() { something = p.blah() }
    
    func someOtherExtensionFunction() { p.blah() }
    // it's ok to use "p" inside other extension functions,
    // and you can use p anywhere in the conforming class
}

В любом соответствующем классе вы теперь «добавили» свойство «p»:

Вы можете использовать «p» так же, как вы бы использовали любое обычное свойство в соответствующем классе. Пример:

class Clock:UIViewController, Able {
    var u:Int = 0

    func blah() {
      u = ...
      ... = u
      // use "p" as you would any normal property
      p = ...
      ... = p
    }

    override func viewDidLoad() {
      super.viewDidLoad()
      pm = .none // "p" MUST be "initialized" somewhere in Clock 
    }
}

Заметка. Вы ДОЛЖНЫ инициализировать псевдо-свойство.

Xcode не будет принудительно выполнять инициализацию «p» в соответствующем классе.

Очень важно, чтобы вы инициализировали «p», возможно, в viewDidLoad подтверждающего класса.

Стоит помнить, что p на самом деле просто вычисленное свойство . p на самом деле просто две функции с синтаксическим сахаром. В любом месте нет «переменной»: компилятор не «назначает некоторую память для p» в каком-либо смысле. По этой причине бессмысленно ожидать, что Xcode обеспечит «инициализацию p».

Действительно, чтобы говорить более точно, вы должны помнить, что «используйте p в первый раз, как если бы вы его инициализировали». (Опять же, это, скорее всего, будет в вашем коде viewDidLoad.)

Что касается геттера как такового.

Обратите внимание, что он будет сбой, если вызывается геттер до того, как будет установлено значение для «p».

Чтобы избежать этого, рассмотрите код, например:

    get {
        let g = objc_getAssociatedObject(self, &_Handle)
        if (g == nil) {
            objc_setAssociatedObject(self, &_Handle, _default initial value_, .OBJC_ASSOCIATION)
            return _default initial value_
        }
        return objc_getAssociatedObject(self, &_Handle) as! YourType
        }

Повторить. Xcode не будет принудительно выполнять инициализацию p в соответствующем классе. Очень важно, чтобы вы инициализировали p, например, в viewDidLoad соответствующего класса.

Сделать код проще ...

Вы можете использовать эти две глобальные функции:

func _aoGet(_ ss: Any!, _ handlePointer: UnsafeRawPointer!, _ safeValue: Any!)->Any! {
    let g = objc_getAssociatedObject(ss, handlePointer)
    if (g == nil) {
        objc_setAssociatedObject(ss, handlePointer, safeValue, .OBJC_ASSOCIATION_RETAIN)
        return safeValue
    }
    return objc_getAssociatedObject(ss, handlePointer)  
}

func _aoSet(_ ss: Any!, _ handlePointer: UnsafeRawPointer!, _ val: Any!) {
    objc_setAssociatedObject(ss, handlePointer, val, .OBJC_ASSOCIATION_RETAIN)
}

Обратите внимание, что они ничего не делают, кроме сохранения ввода и делают код более удобочитаемым. (Они по сути являются макросами или встроенными функциями.)

Тогда ваш код будет выглядеть следующим образом:

protocol PMable: class {
    var click:UILabel? { get set } // ordinary properties here
}

var _pHandle: UInt8 = 321

extension PMable where Self:UIViewController {

    var p:P {
        get {
            return _aoGet(self, &_pHandle, P() ) as! P
        }
        set {
            _aoSet(self, &_pHandle, newValue)
            __pmSetter()
        }
    }
    
    func __pmSetter() {
        click!.text = String(p)
    }
    
    func someFunction() {
        p.blah()
    }
}

(В примере в _aoGet, P является initalizable: вместо P () вы можете использовать «», 0 или любое значение по умолчанию.)



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow