iOS KVO 与 readonly的讨论 (数组array &

作者: 童话镇里蜿蜒的河 | 来源:发表于2019-08-06 15:08 被阅读21次

在开发过程中,可能会有这样的需求:当数据源变动的时候及时刷新显示的列表。
期望是去监听数据源数组的count,当count有变动就刷新UI,可是实际操作中却发现了不少的问题。
例如:

  self.propertyArray = [NSMutableArray array];
  [self.propertyArray addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];

直接就报错了,信息如下:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<__NSArrayM 0x6000033db450> addObserver:forKeyPath:options:context:] is not supported. Key path: count'

字面意思是,不支持对数组count的监听。
回到问题的本质。
我们知道KVO是在属性的setter方法上做文章,进入到数组的类中看一下,发现count属性是readonly

@interface NSArray<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

@property (readonly) NSUInteger count;

readonly不会自动生成setter方法,但是可以手动添加setter方法。
我们来验证一下 例如:
创建一个people类 添加一个属性 readonly count

@interface People : NSObject

@property (nonatomic, readonly) NSInteger count;

@end

@implementation People

@end

我们来试一下 监听它的count属性会怎样

 People *peo = [People new];
 [peo addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];

我们发现 并没有报错。
我们来看一下People的方法列表

    const char *className = "People";
    Class NSKVONotifying_People = objc_getClass(className);
    unsigned int methodCount =0;
    Method* methodList = class_copyMethodList(NSKVONotifying_People,&methodCount);
    NSMutableArray *methodsArray = [NSMutableArray arrayWithCapacity:methodCount];
    
    for(int i=0;i<methodCount;i++)
    {
        Method temp = methodList[i];
        const char* name_s =sel_getName(method_getName(temp));
        [methodsArray addObject:[NSString stringWithUTF8String:name_s]];
    }
    NSLog(@"%@",methodsArray);

通过打印我们可以看到 People中只有两个方法

(
dealloc,
count,
)

我们知道,KVO监听某个对象时,会动态生成名字叫做NSKVONotifying_XX的类,并且重写监听对象的setter方法。下面 我们来看下NSKVONotifying_People的方法列表:

    const char *className = "NSKVONotifying_People";
    Class NSKVONotifying_People = objc_getClass(className);
    unsigned int methodCount =0;
    Method* methodList = class_copyMethodList(NSKVONotifying_People,&methodCount);
    NSMutableArray *methodsArray = [NSMutableArray arrayWithCapacity:methodCount];
    
    for(int i=0;i<methodCount;i++)
    {
        Method temp = methodList[i];
        const char* name_s =sel_getName(method_getName(temp));
        [methodsArray addObject:[NSString stringWithUTF8String:name_s]];
    }
    NSLog(@"%@",methodsArray);

打印结果如下:

(
class,
dealloc,
"_isKVOA"
)

可以看到,里面没有count的getter方法,多了个class和isKVO, 当然也没有我们需要的setter方法。但是这样并不会导致crash。

下面我们再试一下,手动在People的.m中 添加上setter方法会怎么样:

@implementation People

- (void)setCount:(NSInteger)count{
    _count = count;
}

@end

再次查看People和NSKVONotifying_People的方法列表,会发现多了一个count的setter方法。(如下所示)

(
dealloc,
count,
"setCount:"
)
(
"setCount:",
class,
dealloc,
"_isKVOA"
)

这样我们就可以得出一个结论:

KVO动态生成的类,重写setter方法的前提是:原来的类中,要有对应的setter方法。即便是readonly修饰,只要.m中有对应属性的setter方法,都是可以的。

OK 说了这么多,好像还是没有解决我们的问题。 为什么监听数组count就抛异常了呢? 带着这个问题 继续往下走。
通过点击array的监听方法 进入到ArrayObserving类中,我们发现,系统给出了注释:NSArrays are not observable, so these methods raise exceptions when invoked on NSArrays.


arrayDefin.png

系统也不期望我们去监听数组的属性。is not supported. Key path: count' 应该就是系统在实现监听方法时,抛出的异常。

最后,我从网上找到了另一个方法

[self mutableArrayValueForKey:@"propertyArray"]

我们可以转换一个思路,不再监听count,选择监听数组本身,当数组变动时刷新页面。

[self addObserver:self forKeyPath:@"propertyArray" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];

[[self mutableArrayValueForKey:@"propertyArray"] addObject:@"a"];

这个方法的具体实现,没有看到源码,但是看到有人说,这个方法会生成一个可变数组,添加完元素后,会将这个生成的数组赋值给叫做Key的数组。我试了一下,确实是有效果的,这里就不做考究了。

相关文章

  • iOS KVO 与 readonly的讨论 (数组array &

    在开发过程中,可能会有这样的需求:当数据源变动的时候及时刷新显示的列表。期望是去监听数据源数组的count,当co...

  • 使用RAC/KVO监听数组的变化

    iOS默认不支持KVO的形式来监听数组的变化,数组改变的时候,只是数组里面的值变化,但数组的地址没有变化,KVO监...

  • iOS - KVO

    [toc] 参考 KVO KVC 【 iOS--KVO的实现原理与具体应用 】 【 IOS-详解KVO底层实现 】...

  • KVO

    今天和大家讨论一下OC中KVO(KeyValueObserving)键值观察 KVO定义 KVO是iOS开发中的一...

  • IOS 数组的一些操作

    ios可变数组的所有操作 #pragma mark 创建数组c NSMutableArray * array =[...

  • iOS-KVO监听readonly属性

    本文不是技术向的文章,仅记录小弟我在开发中遇到的各种坑... 1.属性 先声明一个只读属性name@propert...

  • iOS KVO 数组

    KVO数组目前都是用array 包裹在一个model中 添加监听 添加元素 可以在当前类直接定义 NSMutabl...

  • iOS KVO 基础与底层原理

    iOS KVO 基础与底层原理 KVO基础 KVO是通过给对象object的属性property注册observe...

  • IOS KVO原理解析与应用

    IOS KVO原理解析与应用 一、KVO概述 KVO,即:Key-Value Observing,是Objecti...

  • iOS-KVO(二) 使用注意点

    iOS-KVO(一) 基本操作iOS-KVO(二) 使用注意点iOS-KVO(三) 窥探底层实现iOS-KVO(四...

网友评论

    本文标题:iOS KVO 与 readonly的讨论 (数组array &

    本文链接:https://www.haomeiwen.com/subject/ievidctx.html