Saya mencoba melakukan pembacaan dan penulisan secara bersamaan dengan aman di database Realm. Inilah yang ingin saya capai.
Saya mengambil gambar dari Flickr, dan setelah imageData
diunduh, objek Photo
ditulis ke dalam database Realm. Saya juga menyertakan notification
untuk mendengarkan insertions
. Setelah objek Foto ditulis ke Realm, perbarui properti transport
item yang sama. Namun implementasi saya terkadang crash, yaitu crash setiap 3-5 kali implementasi.
Kode seperti itu:
override func viewDidLoad() {
super.viewDidLoad()
subscribeToRealmNotifications()
}
fileprivate func subscribeToRealmNotifications() {
do {
let realm = try Realm()
let results = realm.objects(Photo.self)
token = results.observe({ (changes) in
switch changes {
case .initial:
self.setupInitialData()
self.collectionView.reloadData()
case .update(_, _, let insertions, _):
if !insertions.isEmpty {
self.handleInsertionsWhenNotified(insertions: insertions)
}
case .error(let error):
self.handleError(error as NSError)
}
})
} catch let error {
NSLog("Error subscribing to Realm Notifications: %@", error.localizedDescription)
}
}
fileprivate func handleInsertionsWhenNotified(insertions: [Int]) {
let lock = NSLock()
let queue = DispatchQueue(label: "queue", qos: .userInitiated) //Serial queue
queue.async(flags: .barrier) {
do {
let realm = try Realm()
let objects = realm.objects(Photo.self)
lock.lock()
for insertion in insertions {
print(insertion, objects.count, objects[insertion].id ?? "")
let photo = objects[insertion] //Crash here
self.update(photo: photo)
}
lock.unlock()
} catch let error {
NSLog("Error updating photos in Realm Notifications", error.localizedDescription)
}
}
}
func update(photo: Photo) {
do {
let realm = try Realm()
let updatedPhoto = createCopy(photo: photo)
let transport = Transport()
transport.name = searchText
updatedPhoto.transport = transport
try realm.write {
realm.add(updatedPhoto, update: true)
}
} catch let error {
NSLog("Error updating photo name on realm: %@", error.localizedDescription)
}
}
func createCopy(photo: Photo) -> Photo {
let copiedPhoto = Photo()
copiedPhoto.id = photo.id
copiedPhoto.farm = photo.farm
copiedPhoto.server = photo.server
copiedPhoto.secret = photo.secret
copiedPhoto.imageData = photo.imageData
copiedPhoto.name = photo.name
return copiedPhoto
}
//On push of a button, call fetchPhotos to download images.
fileprivate func fetchPhotos() {
FlickrClient.shared.getPhotoListWithText(searchText, completion: { [weak self] (photos, error) in
self?.handleError(error)
guard let photos = photos else {return}
let queue = DispatchQueue(label: "queue1", qos: .userInitiated , attributes: .concurrent)
queue.async {
for (index, _) in photos.enumerated() {
FlickrClient.shared.downloadImageData(photos[index], { (data, error) in
self?.handleError(error)
if let data = data {
let photo = photos[index]
photo.imageData = data
self?.savePhotoToRealm(photo: photo)
DispatchQueue.main.async {
self?.photosArray.append(photo)
if let count = self?.photosArray.count {
let indexPath = IndexPath(item: count - 1, section: 0)
self?.collectionView.insertItems(at: [indexPath])
}
}
}
})
}
}
})
}
fileprivate func savePhotoToRealm(photo: Photo) {
do {
let realm = try Realm()
let realmPhoto = createCopy(photo: photo)
try realm.write {
realm.add(realmPhoto)
print("Successfully saved photo:", photo.id ?? "")
}
} catch let error {
print("Error writing to photo realm: ", error.localizedDescription)
}
}
Perhatikan bahwa kode di atas mogok setiap 3-5 kali sekali, jadi saya curiga pembacaan dan penulisan tidak dilakukan dengan aman. Log cetak dan log kesalahan seperti yang ditunjukkan ketika terjadi kerusakan
Successfully saved photo: 45999333945
4 6 31972639607
6 7 45999333945
Successfully saved photo: 45999333605
Successfully saved photo: 45999333675
7 8 45999333605
8 9 45999333675
Successfully saved photo: 45999333285
Successfully saved photo: 33038412228
2019-01-29 14:46:09.901088+0800 GCDTutorial[24139:841805] *** Terminating app due to uncaught exception 'RLMException', reason: 'Index 9 is out of bounds (must be less than 9).'
Adakah yang bisa membantu memberi tahu di mana kesalahan saya?
CATATAN: Saya telah mencoba menjalankan queue.sync
di handleInsertionsWhenNotified
. Melakukan hal ini akan menghilangkan kerusakan total, namun membekukan UI saat berjalan di thread utama. Ini tidak ideal dalam kasus saya.