Ios 如何为只读派生的NSArray属性实现KVO?

Ios 如何为只读派生的NSArray属性实现KVO?,ios,objective-c,key-value-observing,Ios,Objective C,Key Value Observing,我想为声明为readonly的NSArray属性实现KVO。此readonly属性的getter返回私有NSMutableArray的副本,该数组支持公共readonly一个: 在my.h中: @interface MyClass : NSObject @property (readonly, nonatomic) NSArray *myArray; - (void)addObjectToMyArray:(NSObject *)obj; - (void)removeObjectFromMyArr

我想为声明为
readonly
NSArray
属性实现KVO。此
readonly
属性的getter返回私有
NSMutableArray
的副本,该数组支持公共
readonly
一个:

在my
.h
中:

@interface MyClass : NSObject
@property (readonly, nonatomic) NSArray *myArray;
- (void)addObjectToMyArray:(NSObject *)obj;
- (void)removeObjectFromMyArray:(NSObject *)obj;
@end
在我的
.m
中:

@interface MyClass()
@property (strong, nonatomic) NSMutableArray *myPrivateArray;
@end

@implementation MyClass

- (NSArray *)myArray {
    return (NSArray *)[self.myPrivateArray copy];
}

- (void) addObjectToMyArray:(NSObject *)obj {
    [self willChangeValueForKey:@"myArray"];
    [self.myPrivateArray addObject:obj];
    [self didChangeValueForKey:@"myArray"];
}

- (void) removeObjectToMyArray:(NSObject *)obj {
    [self willChangeValueForKey:@"myArray"];
    [self.myPrivateArray removeObject:obj];
    [self didChangeValueForKey:@"myArray"];
}
@end

在我的测试中,当我调用
didChangeValueForKey:
时,我看到一个异常抛出。这是正确的方法吗?

根据KVO文档,您需要实现
自动通知sobserversforkey
,类似于

    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

BOOL automatic = NO;
if ([theKey isEqualToString:@"myArray"]) {
    automatic = NO;
}
else {
    automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}


我尚未测试此代码,因此如果我走错了方向,我深表歉意。

根据KVO文档,您需要实现
自动通知Sobserversforkey
,类似于

    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

BOOL automatic = NO;
if ([theKey isEqualToString:@"myArray"]) {
    automatic = NO;
}
else {
    automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}


我还没有测试过这段代码,因此如果我走错了方向,我深表歉意。

我对此没有太多经验,但我发布此答案,希望它能解决您的问题或引导您找到解决方案。在过去,我曾使用过:

-(void)viewDidLoad{
    [self addObserver:self forKeyPath:kYoutubeObserverKey options:NSKeyValueObservingOptionNew context:nil];
}

-(void) addLocatedYoutubeURLToList:(NSString *)youtubeURL{

    // -- KVO Update of Youtube Links -- //
    [self willChangeValueForKey:kYoutubeObserverKey
                withSetMutation:NSKeyValueUnionSetMutation
                   usingObjects:[NSSet setWithObject:youtubeURL]];

    [self.youtubeLinksSet addObject:youtubeURL];

    [self didChangeValueForKey:kYoutubeObserverKey
               withSetMutation:NSKeyValueUnionSetMutation
                  usingObjects:[NSSet setWithObject:youtubeURL]];
}
kYoutubeObserverKey
对应于:

static NSString * const kYoutubeObserverKey = @"youtubeLinksSet";
我在这个类中使用了一个同名的属性,因此使用了keyvalue名称:

@property (strong, nonatomic) NSMutableSet * youtubeLinksSet;
我将为您的密钥添加一个观察者,并指定您对观察感兴趣的更改。此外,我会保持密钥命名的一致性,这意味着如果您要更新私钥,那么请注意私钥,而不是公钥。当观察者检测到私钥中的更改时,将因此更新您的公钥。例如:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{

    NSNumber * keyValueChangeType = change[@"kind"];
    if ([keyValueChangeType integerValue] == NSKeyValueChangeInsertion) {

        if ([keyPath isEqualToString:kYoutubeObserverKey] ) {
            //code and such...
        }
    }
}

我在这方面没有太多的经验,但我发布了这个答案,希望它能解决您的问题或引导您找到解决方案。在过去,我曾使用过:

-(void)viewDidLoad{
    [self addObserver:self forKeyPath:kYoutubeObserverKey options:NSKeyValueObservingOptionNew context:nil];
}

-(void) addLocatedYoutubeURLToList:(NSString *)youtubeURL{

    // -- KVO Update of Youtube Links -- //
    [self willChangeValueForKey:kYoutubeObserverKey
                withSetMutation:NSKeyValueUnionSetMutation
                   usingObjects:[NSSet setWithObject:youtubeURL]];

    [self.youtubeLinksSet addObject:youtubeURL];

    [self didChangeValueForKey:kYoutubeObserverKey
               withSetMutation:NSKeyValueUnionSetMutation
                  usingObjects:[NSSet setWithObject:youtubeURL]];
}
kYoutubeObserverKey
对应于:

static NSString * const kYoutubeObserverKey = @"youtubeLinksSet";
我在这个类中使用了一个同名的属性,因此使用了keyvalue名称:

@property (strong, nonatomic) NSMutableSet * youtubeLinksSet;
我将为您的密钥添加一个观察者,并指定您对观察感兴趣的更改。此外,我会保持密钥命名的一致性,这意味着如果您要更新私钥,那么请注意私钥,而不是公钥。当观察者检测到私钥中的更改时,将因此更新您的公钥。例如:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{

    NSNumber * keyValueChangeType = change[@"kind"];
    if ([keyValueChangeType integerValue] == NSKeyValueChangeInsertion) {

        if ([keyPath isEqualToString:kYoutubeObserverKey] ) {
            //code and such...
        }
    }
}

这是脆弱的,
-(NSArray*)myArray
不断为同一个数组返回不同的值,KVO不喜欢这些值

最好定义一个私有可变数组和一个公共只读数组。对可变数组进行更改时,会出现以下错误:

self.myPublicReadOnlyArray=self.myMutableArray.copy;

通过这种方式,您可以避免所有的will/has changed通知,因为
self.myPublicReadOnlyArray
是KVC/KVO兼容的。

这是脆弱的,并且
-(NSArray*)myArray
不断为KVO不喜欢的同一数组返回不同的值

最好定义一个私有可变数组和一个公共只读数组。对可变数组进行更改时,会出现以下错误:

self.myPublicReadOnlyArray=self.myMutableArray.copy;

这样可以避免所有的will/has changed通知,因为
self.myPublicReadOnlyArray
符合KVC/KVO。

我建议不要对可变数组使用单独的属性。相反,让数组属性由可变数组变量支持。然后,实现并通过这些对阵列进行所有更改。KVO知道如何连接到这些访问器并发出更改通知。事实上,它可以发出更好、更具体的更改通知,让观察者能够更有效地做出响应

@interface MyClass : NSObject
@property (readonly, copy, nonatomic) NSArray *myArray;
- (void)addObjectToMyArray:(NSObject *)obj;
- (void)removeObjectFromMyArray:(NSObject *)obj;
@end

@interface MyClass()
// Optional, if you want to be able to do self.myArray = <whatever> in your implementation
@property (readwrite, copy, nonatomic) NSArray *myArray;
@end

@implementation MyClass
{
    NSMutableArray *_myArray;
}

@synthesize myArray = _myArray;

// If you optionally re-declared the property read-write internally, above
- (void) setMyArray:(NSArray*)array {
    if (array != _myArray) {
        _myArray = [array mutableCopy];
    }
}

- (void) insertObject:(id)anObject inMyArrayAtIndex:(NSUInteger)index {
    [_myArray insertObject:anObject atIndex:index];
}

- (void) removeObjectFromMyArrayAtIndex:(NSUInteger)index {
    [_myArray removeObjectAtIndex:index];
}

- (void) addObjectToMyArray:(NSObject *)obj {
    [self insertObject:obj inMyArrayAtIndex:_myArray.count];
}

- (void) removeObjectToMyArray:(NSObject *)obj {
    NSUInteger index = [_myArray indexOfObject:obj];
    if (index != NSNotFound)
        [self removeObjectFromMyArrayAtIndex:index];
}
@end
@接口MyClass:NSObject
@属性(只读、复制、非原子)NSArray*myArray;
-(void)addObjectToMyArray:(NSObject*)obj;
-(void)从MyArray中移除object:(NSObject*)obj;
@结束
@接口MyClass()
//可选,如果您希望能够在实现中执行self.myArray=
@属性(读写、复制、非原子)NSArray*myArray;
@结束
@MyClass的实现
{
NSMutableArray*_myArray;
}
@合成myArray=\u myArray;
//如果您可以选择在内部重新声明属性read-write,请参见上文
-(void)setMyArray:(NSArray*)数组{
如果(数组!=\u myArray){
_myArray=[array mutableCopy];
}
}
-(void)insertObject:(id)MyArrayAtIndex:(nsInteger)索引中的一个对象{
[\u myArray insertObject:anObject atIndex:index];
}
-(void)从MyArrayAtIndex中移除Objects:(nsInteger)索引{
[[u myArray removeObjectAtIndex:index];
}
-(void)addObjectToMyArray:(NSObject*)obj{
[自插入对象:obj inMyArrayAtIndex:_myArray.count];
}
-(void)removeObjectToMyArray:(NSObject*)对象{
NSUInteger索引=[\u myArray indexOfObject:obj];
如果(索引!=NSNotFound)
[来自MyArrayatIndex的自移除对象:索引];
}
@结束

我建议不要对可变数组使用单独的属性。相反,让数组属性由可变数组变量支持。然后,实现并通过这些对阵列进行所有更改。KVO知道如何连接到这些访问器并发出更改通知。事实上,它可以发出更好、更具体的更改通知,让观察者能够更有效地做出响应

@interface MyClass : NSObject
@property (readonly, copy, nonatomic) NSArray *myArray;
- (void)addObjectToMyArray:(NSObject *)obj;
- (void)removeObjectFromMyArray:(NSObject *)obj;
@end

@interface MyClass()
// Optional, if you want to be able to do self.myArray = <whatever> in your implementation
@property (readwrite, copy, nonatomic) NSArray *myArray;
@end

@implementation MyClass
{
    NSMutableArray *_myArray;
}

@synthesize myArray = _myArray;

// If you optionally re-declared the property read-write internally, above
- (void) setMyArray:(NSArray*)array {
    if (array != _myArray) {
        _myArray = [array mutableCopy];
    }
}

- (void) insertObject:(id)anObject inMyArrayAtIndex:(NSUInteger)index {
    [_myArray insertObject:anObject atIndex:index];
}

- (void) removeObjectFromMyArrayAtIndex:(NSUInteger)index {
    [_myArray removeObjectAtIndex:index];
}

- (void) addObjectToMyArray:(NSObject *)obj {
    [self insertObject:obj inMyArrayAtIndex:_myArray.count];
}

- (void) removeObjectToMyArray:(NSObject *)obj {
    NSUInteger index = [_myArray indexOfObject:obj];
    if (index != NSNotFound)
        [self removeObjectFromMyArrayAtIndex:index];
}
@end
@接口MyClass:NSObject
@属性(只读、复制、非原子)NSArray*myArray;
-(void)addObjectToMyArray:(NSObject*)obj;
-(void)从MyArray中移除object:(NSObject*)obj;
@结束
@接口MyClass()
//可选,如果您希望能够在实现中执行self.myArray=
@属性(读写、复制、非原子)NSArray*myArray;
@结束
@MyClass的实现
{
NSMutableArray*_myArray;
}
@合成myArray=\u myArray;
//如果您可以选择在内部重新声明属性read-write,请参见上文
-(void)setMyArray:(NSArray*)数组{
如果(数组!=\u myArray){
_myArray=[array mutableCopy];
}
}
-(void)insertObject:(id)MyArrayAtIndex:(nsInteger)索引中的一个对象{
[\u myArray insertObject:anObject atIndex:index];
}
-(void)从MyArrayAtIndex中移除Objects:(nsInteger)索引