与Swift领域的继承,混乱
Inheritance with Swift Realm, confusion
我对 Realm 中的对象的继承有疑问。
你能看看吗。我有:
- 一个对象
Activity
- 我想成为
Activity
的子类的对象 Sport
- 我想成为
Activity
的子类的对象 Seminar
为了做到这一点,我根据文档编写了以下代码:
// Base Model
class Activity: Object {
dynamic var id = ""
dynamic var date = NSDate()
override static func primaryKey() -> String? {
return "id"
}
}
// Models composed with Activity
class Nutrition: Object {
dynamic var activity: Activity? = nil
dynamic var quantity = 0
}
class Sport: Object {
dynamic var activity: Activity? = nil
dynamic var quantity = 0
dynamic var duration = 0
}
现在我有一个模型 Category
,我希望它能举办活动,不管它是 Nutrition
还是 Sport
。
这是我的代码:
class Categorie: Object {
let activities = List<Activitie>()
dynamic var categoryType: String = ""
override static func primaryKey() -> String? {
return "categoryType"
}
}
现在我尝试通过这样做将 Nutrition
对象添加到我的 List<Activitie>
中:
let nutrition = Nutrition(value: [ "activity": [ "date": NSDate(), "id": "0" ], "quantity": 12 ])
try! realm.write {
realm.add(nutrition, update: true)
}
它不起作用,因为 List<Activitie>
需要一个 Activity
对象而不是 Nutrition
对象。我哪里错了?
非常感谢您的帮助。
您遇到了 Realm 的一大问题:没有完全的多态性。
This github post 重点介绍了哪些是可能的,哪些是不可能的,以及一些您可以使用的可能的解决方案。
上面 link 中来自 jpsim 的快速引用:
Inheritance in Realm at the moment gets you:
- Class methods, instance methods and properties on parent classes are
inherited in their child classes.
- Methods and functions that take
parent classes as arguments can operate on subclasses.
It does not get you:
- Casting between polymorphic classes (subclass->subclass,
subclass->parent, parent->subclass, etc.).
- Querying on multiple classes simultaneously.
- Multi-class container (RLMArray/List and RLMResults/Results).
在您的代码中 Nutrition
和 Sport
不继承 Activity
,它们继承 Object
并合成一个 Activity
实例。要继承 Activity
,您应该
class Nutrition: Activity {
dynamic var quantity = 0
}
我想你可能担心如果你这样做了,你的代码不再继承 Object
。但事实并非如此。 Nutrition
仍然继承自 Object
,因为 Object
继承自 Activity
。
他们的关系是营养:Activity:对象。
Owen 是正确的,关于 OO 原则,我也注意到,您并没有真正进行继承。
当一个对象使用另一个作为属性或属性时,它是关联,而不是继承。我也在审查 Realm 是否支持 Table/Object 级别的继承,就像 Java 对 Hibernate 所做的那样......但并不期望它。
这个框架虽然年轻但功能强大,足以让我避免使用 SQLite ...非常快速、易于使用并且更容易进行数据模型迁移!
正如 Yoam Farges 指出的那样,领域 不支持 :
Casting between polymorphic classes (subclass->subclass,
subclass->parent, parent->subclass, etc.).
这就是我想要做的。
你们说我的 Inheritance 不是 Inheritance 是对的,但是正如你们在 Realm documentation 中看到的那样,这就是你们实现它的方式。
感谢我在 this github post 中获得的信息,我可以实现我想要的并且可以保持易读性。
为多态关系使用选项类型:
class PolyActivity: Object {
dynamic var nutrition: Nutrition? = nil
dynamic var sport: Sport? = nil
dynamic var id = ""
override static func primaryKey() -> String? {
return "id"
}
}
创建我的主要对象Activity
class Activity: Object {
dynamic var date = NSDate()
}
并让我的 Nutrition
和 Sport
对象正确继承到 Activity
class Nutrition: Activity {
dynamic var quantity = 0
}
class Sport: Activity {
dynamic var quantity = 0
dynamic var duration = 0
}
我的 Category
对象现在可以容纳一个列表
class Categorie: Object {
let activities = List<PolyActivity>()
var categoryType: String = ""
override static func primaryKey() -> String? {
return "categoryType"
}
}
这就是我创建 Nutrition
对象的方式:
let polyActivity = PolyActivity(value : [ "id": primaryKey ] )
poly.nutrition = Nutrition(value: [ "date": NSDate(), "quantity": 0, "duration": 0 ])
let category = Category(value: ["categoryType" : "0"])
category.activities.append(polyActivity)
并且只需使用可选绑定即可检索:
if let nutrition = category.activities[0].nutrition { }
如果你们有更好、更清晰、更简单的解决方案,请继续!
根据关于 type erased wrappers in swift and the #5 option 的文章,我最终得到了一些更灵活的东西,这是我的解决方案。
(请注意解决方案#5 需要为 Swift 3 更新,我的解决方案已更新为 Swift 3 )
我的主要对象Activity
class Activity: Object {
dynamic var id = ""
override static func primaryKey() -> String? {
return "id"
}
}
和我的遗产:Nutrition
和 Sport
class 营养:Activity { }
class 运动:Activity { }
根据解决方案#5 option的解决方案:为多态关系使用类型擦除的包装器。
如果您想存储 Activity 的任何子 class 的实例,定义一个类型擦除的包装器来存储类型的名称和主键。
class AnyActivity: Object {
dynamic var typeName: String = ""
dynamic var primaryKey: String = ""
// A list of all subclasses that this wrapper can store
static let supportedClasses: [Activity.Type] = [
Nutrition.self,
Sport.self
]
// Construct the type-erased activity from any supported subclass
convenience init(_ activity: Activity) {
self.init()
typeName = String(describing: type(of: activity))
guard let primaryKeyName = type(of: activity).primaryKey() else {
fatalError("`\(typeName)` does not define a primary key")
}
guard let primaryKeyValue = activity.value(forKey: primaryKeyName) as? String else {
fatalError("`\(typeName)`'s primary key `\(primaryKeyName)` is not a `String`")
}
primaryKey = primaryKeyValue
}
// Dictionary to lookup subclass type from its name
static let methodLookup: [String : Activitie.Type] = {
var dict: [String : Activity.Type] = [:]
for method in supportedClasses {
dict[String(describing: method)] = method
}
return dict
}()
// Use to access the *actual* Activitie value, using `as` to upcast
var value: Activitie {
guard let type = AnyActivity.methodLookup[typeName] else {
fatalError("Unknown activity `\(typeName)`")
}
guard let value = try! Realm().object(ofType: type, forPrimaryKey: primaryKey) else {
fatalError("`\(typeName)` with primary key `\(primaryKey)` does not exist")
}
return value
}
}
现在,我们可以创建一个类型来存储 AnyActivity!
class Category: Object {
var categoryType: String = ""
let activities = List<AnyActivity>()
override static func primaryKey() -> String? {
return "categoryType"
}
}
并存储数据:
let nutrition = Nutrition(value : [ "id" : "a_primary_value"] )
let category = Category(value: ["categoryType" : "0"])
category.activities.append(AnyActivity(tree))
要读取数据我们要检查activity方法,在AnyActivity
上使用value
属性
for activity in activities {
if let nutrition = activity.value as? Nutrition {
// cool it's a nutrition
} else if let sport = activity.value as? Sport {
// cool it's a Sport
} else {
fatalError("Unknown payment method")
}
}
我对 Realm 中的对象的继承有疑问。
你能看看吗。我有:
- 一个对象
Activity
- 我想成为
Activity
的子类的对象 - 我想成为
Activity
的子类的对象
Sport
Seminar
为了做到这一点,我根据文档编写了以下代码:
// Base Model
class Activity: Object {
dynamic var id = ""
dynamic var date = NSDate()
override static func primaryKey() -> String? {
return "id"
}
}
// Models composed with Activity
class Nutrition: Object {
dynamic var activity: Activity? = nil
dynamic var quantity = 0
}
class Sport: Object {
dynamic var activity: Activity? = nil
dynamic var quantity = 0
dynamic var duration = 0
}
现在我有一个模型 Category
,我希望它能举办活动,不管它是 Nutrition
还是 Sport
。
这是我的代码:
class Categorie: Object {
let activities = List<Activitie>()
dynamic var categoryType: String = ""
override static func primaryKey() -> String? {
return "categoryType"
}
}
现在我尝试通过这样做将 Nutrition
对象添加到我的 List<Activitie>
中:
let nutrition = Nutrition(value: [ "activity": [ "date": NSDate(), "id": "0" ], "quantity": 12 ])
try! realm.write {
realm.add(nutrition, update: true)
}
它不起作用,因为 List<Activitie>
需要一个 Activity
对象而不是 Nutrition
对象。我哪里错了?
非常感谢您的帮助。
您遇到了 Realm 的一大问题:没有完全的多态性。
This github post 重点介绍了哪些是可能的,哪些是不可能的,以及一些您可以使用的可能的解决方案。
上面 link 中来自 jpsim 的快速引用:
Inheritance in Realm at the moment gets you:
- Class methods, instance methods and properties on parent classes are inherited in their child classes.
- Methods and functions that take parent classes as arguments can operate on subclasses.
It does not get you:
- Casting between polymorphic classes (subclass->subclass, subclass->parent, parent->subclass, etc.).
- Querying on multiple classes simultaneously.
- Multi-class container (RLMArray/List and RLMResults/Results).
在您的代码中 Nutrition
和 Sport
不继承 Activity
,它们继承 Object
并合成一个 Activity
实例。要继承 Activity
,您应该
class Nutrition: Activity {
dynamic var quantity = 0
}
我想你可能担心如果你这样做了,你的代码不再继承 Object
。但事实并非如此。 Nutrition
仍然继承自 Object
,因为 Object
继承自 Activity
。
他们的关系是营养:Activity:对象。
Owen 是正确的,关于 OO 原则,我也注意到,您并没有真正进行继承。
当一个对象使用另一个作为属性或属性时,它是关联,而不是继承。我也在审查 Realm 是否支持 Table/Object 级别的继承,就像 Java 对 Hibernate 所做的那样......但并不期望它。
这个框架虽然年轻但功能强大,足以让我避免使用 SQLite ...非常快速、易于使用并且更容易进行数据模型迁移!
正如 Yoam Farges 指出的那样,领域 不支持 :
Casting between polymorphic classes (subclass->subclass, subclass->parent, parent->subclass, etc.).
这就是我想要做的。
你们说我的 Inheritance 不是 Inheritance 是对的,但是正如你们在 Realm documentation 中看到的那样,这就是你们实现它的方式。
感谢我在 this github post 中获得的信息,我可以实现我想要的并且可以保持易读性。
为多态关系使用选项类型:
class PolyActivity: Object {
dynamic var nutrition: Nutrition? = nil
dynamic var sport: Sport? = nil
dynamic var id = ""
override static func primaryKey() -> String? {
return "id"
}
}
创建我的主要对象Activity
class Activity: Object {
dynamic var date = NSDate()
}
并让我的 Nutrition
和 Sport
对象正确继承到 Activity
class Nutrition: Activity {
dynamic var quantity = 0
}
class Sport: Activity {
dynamic var quantity = 0
dynamic var duration = 0
}
我的 Category
对象现在可以容纳一个列表
class Categorie: Object {
let activities = List<PolyActivity>()
var categoryType: String = ""
override static func primaryKey() -> String? {
return "categoryType"
}
}
这就是我创建 Nutrition
对象的方式:
let polyActivity = PolyActivity(value : [ "id": primaryKey ] )
poly.nutrition = Nutrition(value: [ "date": NSDate(), "quantity": 0, "duration": 0 ])
let category = Category(value: ["categoryType" : "0"])
category.activities.append(polyActivity)
并且只需使用可选绑定即可检索:
if let nutrition = category.activities[0].nutrition { }
如果你们有更好、更清晰、更简单的解决方案,请继续!
根据关于 type erased wrappers in swift and the #5 option 的文章,我最终得到了一些更灵活的东西,这是我的解决方案。
(请注意解决方案#5 需要为 Swift 3 更新,我的解决方案已更新为 Swift 3 )
我的主要对象Activity
class Activity: Object {
dynamic var id = ""
override static func primaryKey() -> String? {
return "id"
}
}
和我的遗产:Nutrition
和 Sport
class 营养:Activity { }
class 运动:Activity { }
根据解决方案#5 option的解决方案:为多态关系使用类型擦除的包装器。
如果您想存储 Activity 的任何子 class 的实例,定义一个类型擦除的包装器来存储类型的名称和主键。
class AnyActivity: Object {
dynamic var typeName: String = ""
dynamic var primaryKey: String = ""
// A list of all subclasses that this wrapper can store
static let supportedClasses: [Activity.Type] = [
Nutrition.self,
Sport.self
]
// Construct the type-erased activity from any supported subclass
convenience init(_ activity: Activity) {
self.init()
typeName = String(describing: type(of: activity))
guard let primaryKeyName = type(of: activity).primaryKey() else {
fatalError("`\(typeName)` does not define a primary key")
}
guard let primaryKeyValue = activity.value(forKey: primaryKeyName) as? String else {
fatalError("`\(typeName)`'s primary key `\(primaryKeyName)` is not a `String`")
}
primaryKey = primaryKeyValue
}
// Dictionary to lookup subclass type from its name
static let methodLookup: [String : Activitie.Type] = {
var dict: [String : Activity.Type] = [:]
for method in supportedClasses {
dict[String(describing: method)] = method
}
return dict
}()
// Use to access the *actual* Activitie value, using `as` to upcast
var value: Activitie {
guard let type = AnyActivity.methodLookup[typeName] else {
fatalError("Unknown activity `\(typeName)`")
}
guard let value = try! Realm().object(ofType: type, forPrimaryKey: primaryKey) else {
fatalError("`\(typeName)` with primary key `\(primaryKey)` does not exist")
}
return value
}
}
现在,我们可以创建一个类型来存储 AnyActivity!
class Category: Object {
var categoryType: String = ""
let activities = List<AnyActivity>()
override static func primaryKey() -> String? {
return "categoryType"
}
}
并存储数据:
let nutrition = Nutrition(value : [ "id" : "a_primary_value"] )
let category = Category(value: ["categoryType" : "0"])
category.activities.append(AnyActivity(tree))
要读取数据我们要检查activity方法,在AnyActivity
value
属性
for activity in activities {
if let nutrition = activity.value as? Nutrition {
// cool it's a nutrition
} else if let sport = activity.value as? Sport {
// cool it's a Sport
} else {
fatalError("Unknown payment method")
}
}