Я предпочитаю решение, в котором UITableViewCell
выполняет все КВО самостоятельно. Моя установка выглядит следующим образом:
В моем подклассе ячеек у меня есть свойство, которое содержит строгую ссылку на мой класс модели, из которого я извлекаю свои данные, и метод, который я вызываю, когда хочу присоединить новый объект к свойству:
@interface MyTableViewCell : UITableViewCell
@property (atomic) id object;
- (void)populateFromObject:(id)object;
Реализация:
- (void)awakeFromNib {
[super awakeFromNib];
self.contentView.hidden = YES;// avoid displaying an unpopulated cell
}
- (void)populateFromObject:(id)object {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),^{// handle KVO on a bg thread
if (object && (self.object != object)) {// if new object differs from property...
[self unregisterFromKVO];// ...unregister from old object and...
self.object = object;
for (NSString *keyToObserve in [[object class] displayKeys]) {// ...register to new object
[object addObserver:self forKeyPath:keyToObserve options:0 context:nil];
}
}
dispatch_async(dispatch_get_main_queue(), ^{// UI updates on main thread only
// update your outlets here
self.contentView.hidden = NO;// finally display the cell now that it is properly populated
});
});
}
// ===========
#pragma mark - KVO
// ===========
// KVO notification
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
[self populateFromObject:object];
}
- (void)unregisterFromKVO {
for (NSString *keyToObserve in [[self.object class] displayKeys]) {
[self.object removeObserver:self forKeyPath:keyToObserve];
}
}
- (void)dealloc {
[self unregisterFromKVO];
}
Обратите внимание, что фактическое KVO обрабатывается в фоновом потоке, чтобы избежать блокировки основного потока во время прокрутки. Также обратите внимание, что -populateFromObject:
возвращается немедленно и поэтому будет отображать незаполненную ячейку. Чтобы избежать этого, мы скрываем представление контента, пока ячейка не будет заполнена полностью.
Теперь единственное, что осталось реализовать, - это метод класса в YourModelObject
, который возвращает массив ключей, которые вы хотите использовать для KVO:
+ (NSArray<NSString *> *)displayKeys {
return @[@"name",@"Street", @"ZipCode"];
}
.. и в UITableViewController
:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseid" forIndexPath:indexPath];
YourModelObject *obj = [myModelArray objectAtIndex:indexPath.row];
[cell populateFromObject:obj];
return cell;
}
Сильная ссылка от ячейки на объект модели гарантирует, что объект не будет освобожден, пока ячейка все еще наблюдает одно из своих свойств, то есть является видимым. Как только ячейка освобождается, KVO не регистрируется, и только тогда объект модели будет освобожден. Для удобства у меня также есть слабая ссылка от объекта модели обратно на ячейку, которая может пригодиться при реализации UITableView
методов делегата.