我可以使Realm Results类使用协议作为泛型吗?

Bla*_*ard 5 protocols realm ios swift

我想创建两个Realm模型类和一个协议,它由两个模型类采用.例如:

class Dog: Object, Animal {
    dynamic var name = ""
}
class Cat: Object, Animal {
    dynamic var name = ""
}
protocol Animal {
    var name: String { get }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我创建了两个模型类和一个协议.

但是,当我转移到实现时,问题就出现了.下面的代码是在视图控制器中编写的:

var dogs: Results<Dog>? {
    return try! Realm().objects(Dog)
}
var cats: Results<Cat> {
    return try! Realm().objects(Cat)
}
Run Code Online (Sandbox Code Playgroud)

这段代码没有任何问题.但是下面的代码:

var animals: Results<Animal>? {
    switch currentSegmented { // this is from UISegmentedControl
    case .Cat:  // this is from enum
        return self.cats
    case .Dog:
        return self.dogs
}
Run Code Online (Sandbox Code Playgroud)

没有编译错误:Results requires that Animal inherit from Object.

但是,Animal是一个协议,因此不能继承Object.

这里仍然可以使用协议吗?

Tho*_*yne 6

我不认为有一个很好的解决方案.在斯威夫特用户定义泛型是不变的,所以即使Animal是你不能将一个类Results<Dog>Results<Animal>.

令人不快的详细解决方案是围绕不同类型的结果创建一个显式的包装器类型:

enum AnimalResultsEnum {
    case DogResults(dogs: Results<Dog>)
    case CatResults(cats: Results<Cat>)
}

class AnimalResults {
    var animals = AnimalResultsEnum.DogResults(dogs: try! Realm().objects(Dog))

    var realm: Realm? {
        switch animals {
        case .DogResults(let dogs):
            return dogs.realm
        case .CatResults(let cats):
            return cats.realm
        }
    }

    var count: Int {
        switch animals {
        case .DogResults(let dogs):
            return dogs.count
        case .CatResults(let cats):
            return cats.count
        }
    }

    subscript(index: Int) -> Animal {
        switch animals {
        case .DogResults(let dogs):
            return dogs[index]
        case .CatResults(let cats):
            return cats[index]
        }
    }

    // ... wrap the rest of the methods needed ...
}
Run Code Online (Sandbox Code Playgroud)

你可以通过创建一个半类型擦除容器来包装结果来制作这个通用:

class CovariantResults<T: Object> {
    private var base: _CovariantResultsBase<T>

    init<U: Object>(_ inner: Results<U>) {
        base = _CovariantResultsImpl<T, U>(inner)
    }

    subscript(index: Int) -> T {
        return base[index]
    }

    // ... wrap the rest of the methods needed ...
}

class _CovariantResultsBase<T: Object> {
    subscript(index: Int) -> T { fatalError("abstract") }
    // ... wrap the rest of the methods needed ...
}

class _CovariantResultsImpl<T: Object, U: Object>: _CovariantResultsBase<T> {
    private let impl: Results<U>

    init(_ inner: Results<U>) {
        impl = inner
    }

    override subscript(index: Int) -> T {
        return impl[index] as! T
    }

    // ... wrap the rest of the methods needed ...
}

// Used as:
let animals = CovariantResults<Animal>(try! Realm().objects(Dog))
Run Code Online (Sandbox Code Playgroud)