ฉันกำลังพยายามอ่านและเขียนพร้อมกันอย่างปลอดภัยบนฐานข้อมูล Realm นี่คือสิ่งที่ฉันพยายามที่จะบรรลุ
ฉันกำลังดึงภาพจาก Flickr และเมื่อดาวน์โหลด imageData
แล้ว ออบเจ็กต์ Photo
จะถูกเขียนลงในฐานข้อมูล Realm ฉันได้รวม notification
เพื่อฟัง insertions
ไว้ด้วย เมื่อวัตถุภาพถ่ายถูกเขียนไปยัง Realm แล้ว ให้อัปเดตคุณสมบัติ transport
ของรายการเดียวกันนั้น อย่างไรก็ตาม การใช้งานของฉันขัดข้องเป็นครั้งคราว กล่าวคือ ขัดข้องทุกๆ 3-5 ครั้งของการใช้งาน
รหัสดังกล่าว:
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)
}
}
โปรดทราบว่าโค้ดด้านบนขัดข้องทุกๆ 3-5 ครั้ง ดังนั้นฉันสงสัยว่าการอ่านและเขียนไม่เสร็จอย่างปลอดภัย บันทึกการพิมพ์และบันทึกข้อผิดพลาดจะแสดงเมื่อเกิดข้อขัดข้อง
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).'
ใครก็ได้ช่วยบอกทีว่าฉันผิดตรงไหน?
หมายเหตุ: ฉันได้ลองเรียกใช้ queue.sync
ที่ handleInsertionsWhenNotified
แล้ว การทำเช่นนี้จะช่วยลดข้อขัดข้องโดยสิ้นเชิง แต่จะค้าง UI ขณะที่ทำงานบนเธรดหลัก นี่ไม่เหมาะในกรณีของฉัน