我正在使用Realm
作为缓存层,以便无论何时将数据呈现给用户,都首先从数据库中获取数据并显示给用户。随后,发送服务器请求以获取数据的最新版本,将其与Realm
数据库同步,并在UICollectionView
中显示更改。
问题在于,当从Realm
数据库中检索缓存的数据并且UICollectionView
正在更新时,服务器的更新请求有可能在UICollectionView
之前完成加载了所有单元格,并且由于Results
列表是实时数据集合,因此可以对其进行修改。现在,例如,如果在服务器端删除了一项,则实时集合将保留少一项,因此会导致超出范围的异常。
话虽如此,考虑到Realm
可以在results
请求每一行时更改UITableView
的事实,即使是官方class ViewController: UITableViewController {
var notificationToken: NotificationToken? = nil
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
let results = realm.objects(Person.self).filter("age > 5")
// Observe Results Notifications
notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
tableView.reloadData()
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.endUpdates()
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
}
}
deinit {
notificationToken?.invalidate()
}
}
文档中提供的代码也不是线程安全的一位:
tableView.endUpdates()
我想解决此问题的唯一方法是创建结果的深层副本,并使用Semaphore或类似方法同步观察函数的主体,以确保数据不会处于不一致的状态,我考虑效率很低。 (请注意,UITableView
并不意味着@ToString(includeFieldNames=true) @EqualsAndHashCode @AllArgsConstructor
public class PartyDTO<DATE> implements MongoDTO
{
@PersistenceConstructor
public PartyDTO()
{
}
@Id @Getter @Setter @Searchable
private String partyId;
@Getter @Setter
private Meta<DATE> meta;
@Getter
@Setter
@Searchable
private String customerType;
@Getter @Setter
private PartyIdList idList;
@Getter @Setter
private String fullName;
@Getter @Setter
private PartyPerson<DATE> person;
@Getter @Setter @Searchable
private String branchId;
@Getter @Setter @Searchable
private String clientStanding;
@Getter @Setter
private ZonedDateTime clientStandingDate;
@Getter @Setter
private Boolean dayTraderInd;
@Getter @Setter
private List<PartyAltId> altIds;
@Getter @Setter
private List<PartyAltName> altNames;
@Getter
@Setter
private List<Phone> phoneList;
@Getter @Setter
private List<NetContact> netContactList ;
@Getter @Setter
private List<Address> addressList;
@Getter @Setter
private PartySuitability<?> suitability;
@Getter @Setter
private List<PartyPref> prefList;
}
已经重新加载了所有数据,但是只是将其分派到队列中并准备以异步方式进行处理。)
我想听听有关如何以有效方式实施此建议以消除上述比赛条件的任何建议。
答案 0 :(得分:1)
您需要在主线程上进行所有UI更新。如果这样做,则第一组结果将更新主线程上的集合视图,当下一组结果也出现时,它将在主线程上排队,以便在完成第一组结果后进行更新。
答案 1 :(得分:0)
基于:
问题是,当从Realm数据库中检索缓存的数据并且UICollectionView得到更新时,服务器的更新请求有可能在UICollectionView加载所有单元之前完成,并且因为“结果”列表是实时的数据收集,可能已经被修改了。
我不认为会发生这种情况,因为一旦您的实时馆藏发生变化,就会触发更新通知,并且馆藏将相应地重建/更新。但是,正如我在PM中对您所说的那样,是我前一段时间与领域合作的时候。
检验您的假设非常容易:降低模拟器的互联网速度,或腾大桌子等等。我真的很好奇您是否能真正创建自己认为会遇到的问题。