3

我有一个对象可能会将 NSNull 设置为其所有属性的值,并且我正在使用与 NSNull 值不匹配的代码。是否可以像这样在我的对象中覆盖 setValue:forKey:?

-(void)setValue:(id)value forKey:(NSString *)key {

    if ([value isEqual:[NSNull null]]) {
        [super setValue:nil forKey:key];
    } else {
        [super setValue:value forKey:key];
    }
}

设置该方法时不会调用该方法object.property = [NSNull null]。我怎样才能使它成为我的二传手的默认行为?

4

2 回答 2

5

肯定有办法做到这一点。您可以声明相关属性@dynamic,然后用于+resolveInstanceMethod:按需实际创建设置器。您可以在iOS:PTL 的示例代码中找到这样的示例。这个例子演示了如何自动让你的访问器读取和写入字典 ( properties) 而不是从 ivars。为了使这项工作适合您,您可能需要始终存储NSNull在字典中并覆盖 getter ( propertyIMP()) 以将其转换为nil. 所以你会改变这个:

[[self properties] setValue:value forKey:key];

至:

[[self properties] setValue:(value ?: [NSNull null]) forKey:key];

并改变:

return [[self properties] valueForKey:NSStringFromSelector(_cmd)];

至:

id value = [[self properties] valueForKey:NSStringFromSelector(_cmd)];
return (value == [NSNull null] ? nil : value);

或类似的东西。

但是......除非这是一场重大胜利,否则我会避免这种魔法。我对此的典型解决方案是另辟蹊径,将它放在调用者不应该通过的[NSNull null]时候。我使用如下函数RNNonNull()

id RNNonNull(id x) { return (x == [NSNull null]) ? nil : x; }

然后调用者负责添加包装器:

obj.foo = RNNonNull(mystuff);

如果那不可能,我可能会手动覆盖 getter 以转换NSNullnil而不是使用运行时进行转换(这将是一种单行方法)。我使用运行时的唯一原因是如果有一堆属性,并且对象非常简单(纯粹是一个数据对象)。

于 2013-08-22T03:34:40.367 回答
2

设置属性实际上不会导致调用setValue:forKey:. 您可以使用 KVO 完成此操作。例如,

@interface Foo
@property (nonatomic, strong) NSObject *bar;
@end

@implementation

- (id)init {
    self = [super init];
    if (self) {
        [self addObserver:self forKeyPath:@"bar" options:0 context:nil];
    }
    return self;
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"bar"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (self.bar == [NSNull null]) {
        self.bar = nil;
    }
}

因此,只要将属性bar设置为,[NSNull null它就会立即设置为nil.

于 2013-08-22T02:28:28.940 回答