我在一些小项目中一直使用Realm,我非常喜欢它。我希望继续在更大的项目中使用它,我正在寻找更好的结构我的数据访问层。
我遇到了类似的question,并试图建立我在那里找到的信息。在那里讨论的方法是DAO模式,所以我给了它一个镜头。
这是我的模特课。
class Chat: Object {
dynamic var id: String = ""
dynamic var createdAt: Date = Date()
dynamic var creatorId: String = ""
dynamic var title: String?
let chatMessages = List<ChatMessage>()
override static func primaryKey() -> String? {
return "id"
}
convenience init(fromJSON json: JSON) {
self.init()
// ...
}
}
然后我创建了一个ChatDAOProtocol
来保存所有方便助手方法。
protocol ChatDAOProtocol {
func addMessage(_ message: ChatMessage)
func getChatThumbnail() -> UIImage
func getParticipants(includingMe: Bool) -> [Participant]?
static func getChat(fromId id: String) -> Chat?
static func getChat(fromCreatorId id: String) -> Chat?
}
最后,我创建了另一个名为ChatHelper
的类,它实现了所有这些协议方法。
class ChatHelper: ChatDAOProtocol {
func addMessage(_ message: ChatMessage) {
}
func getChatThumbnail() -> UIImage {
return UIImage()
}
func getParticipants(includingMe: Bool) -> [Participant]? {
return nil
}
static func getChat(fromId id: String) -> Chat? {
return nil
}
static func getChat(fromCreatorId id: String) -> Chat? {
return nil
}
}
这似乎比在VC和东西上遍布所有数据库相关代码更好。但我仍然有些疑惑。
例如,如果我需要让所有参与者都参与聊天,现在我必须在ChatHelper
课程上调用该方法。如果我想简单地获得聊天标题,我会调用title
对象本身的Chat
属性。看起来不是一个非常统一的界面。我是否应该在帮助程序中包含所有属性的getter和setter。因此永远不会直接调用Chat
对象(除了可能创建实例)。
或者
我应该使Chat
对象本身符合ChatDAOProtocol
协议吗?所以所有的便捷方法以及属性都可以直接从Chat
对象直接访问?
还是有比这两种更好的方法吗?
答案 0 :(得分:3)
这是一个非常棘手的问题,因为它实际上取决于你想要从与Realm的直接交互中抽象出多少,以及你想要与Realm的性能妥协多少。
就个人而言,我认为如果你抽象掉查询和编写逻辑,但仍然直接从Realm模型对象中读取,那就没问题了。如果你移动到另一个基于对象的数据库(像核心数据),那么你将重构父类,而这些对象属于其他东西(例如,RLMObject
到NSManagedObject
),你的业务逻辑的方式从这些对象中读取不会改变。
你必须要小心的一件事是以一种非常低效地利用Realm的方式抽象逻辑。
我可以看到的主要示例是在getParticipants
方法中,您将返回一个标准的Swift数组。将领域Results
对象转换为这样会导致分页内存中的每个对象(而不是请求中的延迟加载),因此您将失去许多Realm性能优势。但由于Results
对象的行为与标准数组类似,因此如果直接返回,则无需更改业务逻辑。
另一个考虑因素:如果您要更新一批对象上的单个属性,那么最好确保在单个写入事务中更新所有对象,而不是每个内部打开写入事务的助手类调用辅助方法的时间。
答案 1 :(得分:0)
我是Realm的新手,但是我编写了这段代码,以使用通用的方式访问对象。我仍然有一些问题可以从后台线程访问此方法,但希望对您有所帮助!
import Foundation
import RealmSwift
class GenericDAOImpl <T:Object> : GenericDAO {
// MARK: setup
var realm: Realm?
init() {
do {
realm = try Realm()
} catch {
logger.error("Realm Initialization Error: \(error)")
}
}
// MARK: protocol implementation
func save(_ object: T) -> Bool {
guard let `realm` = realm else {
return false
}
do {
try realm.write {
realm.add(object)
}
} catch {
return false
}
return true
}
func saveAll(_ objects: [T]) -> Int {
var count = 0
for obj in objects {
if save(obj) { count += 1 }
}
return count
}
private func findAllResults() -> Results<T>? {
return realm?.objects(T.self)
}
func findAll() -> [T] {
guard let res = findAllResults() else { return [] }
return Array(res)
}
func findByPrimaryKey(_ id: Any) -> T? {
return self.realm?.object(ofType: T.self, forPrimaryKey: id)
}
func deleteAll() {
guard let res = findAllResults() else { return }
do {
try realm?.write {
self.realm?.delete(res)
}
} catch {
logger.error("Realm Error Deleting Objects: \(error)")
return
}
}
}
我忘了展示我的GenericDAO协议:
protocol GenericDAO {
associatedtype T:Object
func save(_ object: T) -> Bool
func saveAll(_ objects: [T]) -> Int
func findAll() -> [T]
func findByPrimaryKey(_ id: Any) -> T?
func deleteAll()
}
要创建实例:
let movieDAO = GenericDAOImpl<Movie>()
我仍在研究这是否是最好的方法,但是无论如何它对我有很大帮助。