在项目,可能会有需求需要监听 NSMutableArray 的变化,例如在可变数组中加入、删除或者替换了元素,我们需要根据这些变化来更新UI或者做其他操作。
那么如何来监听呢?
方法1,使用 mutableArrayValueForKey:
代理,这样,我们在获取定义的数组属性时不再使用其 getter
方法,而是通过代理方法获取数组属性后,再对数组进行增删改的操作。这是最简单高效的方法,使用示例如下:
#import "ViewController.h"
#import "Student.h"
#import "ViewModel.h"static void *xxcontext = &xxcontext;@interface ViewController ()@property (strong, nonatomic) ViewModel *viewModel;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.viewModel = [[ViewModel alloc] init];[self.viewModel addObserver:self forKeyPath:@"students" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:xxcontext];
}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.
}- (void)dealloc {[self.viewModel removeObserver:self forKeyPath:@"students"];
}- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {if (context == xxcontext) {if ([keyPath isEqualToString:@"students"]) {NSNumber *kind = change[NSKeyValueChangeKindKey];NSArray *students = change[NSKeyValueChangeNewKey];NSArray *oldStudent = change[NSKeyValueChangeOldKey];NSIndexSet *changedIndexs = change[NSKeyValueChangeIndexesKey];NSLog(@"kind: %@, students: %@, oldStudent: %@, changedIndexs: %@", kind, students, oldStudent, changedIndexs);}} else {[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];}
}- (IBAction)addStudent:(id)sender {Student *st1 = [[Student alloc] initWithFirstName:@"carya" lastName:@"liu"];Student *st2 = [[Student alloc] initWithFirstName:@"cici" lastName:@"liu"];Student *st3 = [[Student alloc] initWithFirstName:@"ted" lastName:@"liu"];NSArray *students = @[st1, st2, st3];[self.viewModel addStudents:students];[self.viewModel addStudent:st3];
}@end
我们在 viewDidLoad
函数中注册了 viewModel 对象 students 属性的监听者,同时在 dealloc
中移除了监听者。
ViewModel
中定义了 students
属性,如下:
@property (copy, nonatomic) NSMutableArray *students;
主要看看 ViewModel
中添加Student对象的函数实现:
- (NSMutableArray *)studentsArray {return [self mutableArrayValueForKey:NSStringFromSelector(@selector(students))];
}- (void)addStudents:(NSArray *)students {[[self studentsArray] addObjectsFromArray:students];
}- (void)addStudent:(Student *)student {[[self studentsArray] addObject:student];
}
上面的代码中,我们使用代理 mutableArrayValueForKey
替代 getter
来获取 students
属性,- (NSMutableArray *)studentsArray
类似于一个 getter
方法。
ViewController 中定义的 - (IBAction)addStudent:(id)sender
是一个按钮事件,点击该按钮,看看日志输出:
2015-08-25 08:17:29.216 KVO[4235:252903] kind: 2, students: ("<Student: 0x7fe463c89960>"
), oldStudent: (null), changedIndexs: <NSIndexSet: 0x7fe463cac290>[number of indexes: 1 (in 1 ranges), indexes: (0)]
2015-08-25 08:17:29.217 KVO[4235:252903] kind: 2, students: ("<Student: 0x7fe463c3c120>"
), oldStudent: (null), changedIndexs: <NSIndexSet: 0x7fe463d308c0>[number of indexes: 1 (in 1 ranges), indexes: (1)]
2015-08-25 08:17:29.218 KVO[4235:252903] kind: 2, students: ("<Student: 0x7fe463c1fb60>"
), oldStudent: (null), changedIndexs: <NSIndexSet: 0x7fe463d308c0>[number of indexes: 1 (in 1 ranges), indexes: (2)]
2015-08-25 08:17:29.218 KVO[4235:252903] kind: 2, students: ("<Student: 0x7fe463c1fb60>"
), oldStudent: (null), changedIndexs: <NSIndexSet: 0x7fe463d0c0b0>[number of indexes: 1 (in 1 ranges), indexes: (3)]
从上面的示例可以看出:
addObjectsFromArray
向数组中添加元素时,每往数组中添加一个元素都会触发一次 KVO 的执行NSKeyValueChangeInsertion
如果我们想一次性往数组中加入多个元素(如 addObjectsFromArray
),但是只想让其触发一次 KVO 的执行,怎么操作呢?
答案是使用 NSMutableArray 的这个接口 - (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes
, 将 ViewModel 的 - (void)addStudents:(NSArray *)students
函数实现修改成如下:
- (void)addStudents:(NSArray *)students {
// [[self studentsArray] addObjectsFromArray:students];NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange([self studentCount], [students count])];[[self studentsArray] insertObjects:students atIndexes:indexSet];
}
再次运行,就会发现 KVO 只触发了一次。
方法2,遵从属性的 KVC 规则,实现对应操作的方法。先看看对于 NSMutableArray 类型 KVC 方面的文档:
In order to be key-value coding compliant for a mutable ordered to-many relationship you must implement the following methods:
-insertObject:in<Key>AtIndex: or -insert<Key>:atIndexes:. At least one of these methods must be implemented. These are analogous to the NSMutableArray methods insertObject:atIndex: and insertObjects:atIndexes:.
-removeObjectFrom<Key>AtIndex: or -remove<Key>AtIndexes:. At least one of these methods must be implemented. These methods correspond to the NSMutableArray methods removeObjectAtIndex: and removeObjectsAtIndexes: respectively.
-replaceObjectIn<Key>AtIndex:withObject: or -replace<Key>AtIndexes:with<Key>:. Optional. Implement if benchmarking indicates that performance is an issue.
The -insertObject:in<Key>AtIndex: method is passed the object to insert, and an NSUInteger that specifies the index where it should be inserted. The -insert<Key>:atIndexes: method inserts an array of objects into the collection at the indices specified by the passed NSIndexSet. You are only required to implement one of these two methods.
对于上述文档,个人简单理解为,要实现 NSMutableArray 的增删改操作遵从 KVC 的规则,需要实现其对应方法:
-insertObject:in<Key>AtIndex:
或者 -insert<Key>:atIndexes:
-removeObjectFrom<Key>AtIndex:
或者 -remove<Key>AtIndexes:
-replaceObjectIn<Key>AtIndex:withObject:
或者 -replace<Key>AtIndexes:with<Key>:
并将这些接口暴露给调用者,在对数组进行操作时需使用上述实现的接口。
对于方法1中提到的实例,如果想让往 ViewModel 的 students 数组添加元素时触发 KVO 通知的发送,需像如下代码实现上述方法:
- (NSUInteger)studentCount {return [self.students count];
}- (void)insertStudents:(NSArray *)array atIndexes:(NSIndexSet *)indexes {[self.students insertObjects:array atIndexes:indexes];
}- (void)insertObject:(Student *)object inStudentsAtIndex:(NSUInteger)index {[self.students insertObject:object atIndex:index];
}
ViewController 中添加新元素的按钮事件实现更改成如下:
- (IBAction)addStudent:(id)sender {Student *st1 = [[Student alloc] initWithFirstName:@"carya" lastName:@"liu"];Student *st2 = [[Student alloc] initWithFirstName:@"cici" lastName:@"liu"];Student *st3 = [[Student alloc] initWithFirstName:@"ted" lastName:@"liu"];NSArray *students = @[st1, st2, st3];
// [self.viewModel addStudents:students];
// [self.viewModel addStudent:st3];NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange([self.viewModel studentCount], [students count])];[self.viewModel insertStudents:students atIndexes:indexSet];
}
编译重新运行示例,控制台日志输出如下:
2015-08-25 20:50:35.324 KVO[6483:294703] kind: 2, students: ("<Student: 0x7f8508684de0>","<Student: 0x7f8508684e00>","<Student: 0x7f850863d690>"
), oldStudent: (null), changedIndexs: <NSIndexSet: 0x7f850863d6b0>[number of indexes: 3 (in 1 ranges), indexes: (0-2)]
从日志中可以看出,KVO 的通知只触发了一次,从 KVO 的通知中获取到了新添加的3个元素以及新元素在数组中索引。
监听 NSMutableArray 内元素变化的 KVO 总结:
mutableArrayValueForKey:
代理来获取 NSMutableArray 属性另外,从 NSKeyValueCoding Protocol Reference 中可以看出,对于 NSMutableSet 和 NSMutableOrderedSet 也有其代理方法。
参考:
原文地址:
本文发布于:2024-02-04 11:28:16,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170706138855157.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |