简体   繁体   中英

Swift override all setters and getters of a subclass

I would like to override a setter/getter one time for but for all the properties for a class in swift

This my class. I want to call Realm each time I am adding a new value

class House : Object
{
    var a:String
    {
        set {
            do {
                let realm = try Realm()
                try realm.write {
                    a = newValue
                }
            }
            catch {

            }
        }
    }


    var b:String
    {
        set {
            do {
                let realm = try Realm()
                try realm.write {
                    b = newValue
                }
            }
            catch {

            }
        }
    }
}

There is no way in Swift how you can overwrite setters for all properties at once.

What you could generally do though is use:

  1. overwritten setters per property
  2. abstract computed properties wrapping low-level properties
  3. intercept getters and setters by KVC accessor methods (eg is<Key> , get<Key> , …) and rely only on untyped dynamic KVC-based access via valueForKey(Path): , if you want to apply the decorated behavior (which you might want to avoid for this reason)

But Realm is using custom getters and setters under the hood, which are dynamically overwritten in an dynamically inserted intermediate class at runtime and relies on the presence of those. So the only approach, which is really feasible is having dynamic stored properties declared and adding for each of those an extra property, based on those.

 var storedPropertyA: String = ""
 var computedPropertyA: String {
      get {
          // add your extra behavior here
          return storedPropertyA
      }
      set {
          // add your extra behavior here
          self.storedPropertyA = newValue
      }
 }

Beside that there is an alternative way of using the decorator pattern and decorate your whole object with extra behavior. In Swift, you could have your object and your decorator implement a common protocol, which defines your properties.

protocol HousingProperties {
    var a: String { get set }
}

class House: HousingProperties {
    var a: String = ""
}

class HouseDecorator: HousingProperties {
    internal var house: House

    init(house: House) { self.house = house }

    var a: String {
         // add your extra behavior here
         self.house.a = a
    }
}

Still I would NOT recommend to intercept property setters and getters for the purpose you intend here. Instead I'd advise to structure your application's architecture in a way, that allows you to be aware whether there is a write transaction or not and let the responsibility of making a write transaction in the hands of the code, which tries to modify objects.

Let me explain why: Realm is using a multiversion concurrency control algorithm to manage persisted data and achieve thread-safety. This makes sure that different threads can read data at any point in time without having to read-lock and trying to synchronize these. Instead when a write is happening, all accessors are notified that there is new data and try to move on to the newest transaction. Until that has happened, all versions between the oldest data version, which is still used by a thread and the one written have to be retained. They can be first released when all threads advanced their commit pointers. If you do a lot of small transactions, you risk that your file size will blew up to unnecessary high values. For that reason, we recommend to batch write transactions to large changesets.

There is one hack to kind of attain what the poster is looking for, however possibly not advisable... Anyway; you can can create your own assignment operators that does whatever you want to do in realm prior to assigning the values

class MyType {        
    var myInt : Int = 0
    var myString : String = ""

    init(int: Int, string: String) {
        myInt = int
        myString = string
    }
}

infix operator === {}
func ===<T>(lhs: T, rhs: T) -> T? {
    Realm() // replace with whatever Realm()-specific stuff you want to do
    return rhs
}

protocol MyAddableTypes {
    func + (lhs: Self, rhs: Self) -> Self
}

extension String : MyAddableTypes {}
extension Int : MyAddableTypes {}

infix operator +== {} // ... -== similarily
func +==<T: MyAddableTypes>(lhs: T, rhs: T) -> T? {
    Realm() // replace with whatever Realm()-specific stuff you want to do
    return lhs+rhs
}

func Realm() {
    // ...
    print("Called realm")
}

var a = MyType(int: 1, string: "foo")
a.myString === "bar" // calls Realm(). After operation: a.myString = "bar"
a.myInt +== 1 // calls Realm(). After operation: a.myInt = 2

I thought I'd also mention that if you only want to do "Realm stuff" when a value is set (from your example: prior to setting a value, specifically), then the willSet method, used with stored properties, doesn't need to look so messy (nested closures), and personally, I would prefer this method

func Realm() {
    print("Called realm")
}

class MyType {

    // This isn't so messy, is it?
    var myInt : Int = 0 { willSet { priorToSetValue(newValue) } }
    var myString : String = "" { willSet { priorToSetValue(newValue) } }
    var myDouble : Double = 0.0 { willSet { priorToSetValue(newValue) } }

    private func priorToSetValue<T> (myVar: T) {
        // replace with whatever Realm()-specific stuff you want to do,
        // possibly including doing something with your new value
        Realm()
    }

    init(int: Int, double: Double, string: String) {
        myInt = int
        myDouble = double
        myString = string
    }
}

var a = MyType(int: 1, double: 1.0, string: "foo")

a.myString = "bar"
print(a.myString) // calls Realm(). After operation: a.myString = "bar"
a.myInt += 1 // calls Realm(). After operation: a.myInt = 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM