问题: 开发中有个需求是Model中的任一属性被改变,都要通知,做缓存。
解决1
第一个想到的解决方式是重写Model的所有属性set方法。然后做通知逻辑。
显然他有弊端,如增加了属性,重复性的代码非常多。
我的解决方式:
runtime 获取所有属性set方法,并方法交换(AOP)。当set方法触发的时候触发交换的方法。以下代码
使用Aspects hook详情GitHub
/**
hook 属性 set 方法,当属性被修改,触发block
*/
- (void)propertysHook {
// 黑名单属性列表。注意: set 方法有冒号【:】
NSArray *blackPropertyList = @[@"setPropertyChange:"];
// 1 遍历属性列表
for (NSString *selName in [self getObjectPropertyName]) {
// 2 过滤不需要 hook 的属性,如:block 属性,父类与子类同名的属性 hook 会出问题,不要 hook.
if ([blackPropertyList containsObject:selName]) {
continue;
}
// 3 hook 属性
SEL sel = NSSelectorFromString(selName);
[self aspect_hookSelector:sel withOptions:AspectPositionAfter usingBlock:^(id aspectInfo){
// 4 属性改变通知
if (self.propertyChange) self.propertyChange();
} error:NULL];
}
}
/**
获取对象所有属性 set 方法字符串
@return set 方法字符串数组
*/
- (NSMutableArray *)getObjectPropertyName {
NSMutableArray *propertieArray = [NSMutableArray new];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSString *propertyName = [[NSString alloc] initWithFormat:@"%s:", property_getName(property)];
if (!propertyName.length) continue;
NSString *firstPropertyName = [[propertyName substringToIndex:1] uppercaseString];
NSString *lastPropertyName = [propertyName substringFromIndex:1];
propertyName = [NSString stringWithFormat:@"set%@%@", firstPropertyName, lastPropertyName];
[propertieArray addObject:propertyName];
}
free(properties);
return propertieArray;
}