我有一个Core Data实体,我想存储一个CGRect值。因为没有CGRect支持核心数据所以我只是将它分成四个浮动属性:x,y,width,height。
为了使我更方便,我添加了一个自定义属性frame
,它从四个float属性返回组合的CGRect。顺便说一下,我正在使用发电机。
@interface ViewableData : _ViewableData
@property (nonatomic) CGRect frame;
@end
@implementation ViewableData
- (void)setFrame:(CGRect)frame {
if (frame.origin.x != self.xValue)
self.xValue = frame.origin.x;
if (frame.origin.y != self.yValue)
self.yValue = frame.origin.y;
if (frame.size.width != self.widthValue)
self.widthValue = frame.size.width;
if (frame.size.height != self.heightValue)
self.heightValue = frame.size.height;
}
- (CGRect)frame {
return CGRectMake(self.xValue, self.yValue, self.widthValue, self.heightValue);
}
+ (NSSet *)keyPathsForValuesAffectingFrame {
return [NSSet setWithObjects:ViewableDataAttributes.height, ViewableDataAttributes.width, ViewableDataAttributes.x, ViewableDataAttributes.y, nil];
}
@end
它按照我的预期工作,但我发现KVO观察者得到的通知次数超过了它的需要。设置一个帧可以通知观察者4次。无论如何要避免不必要的KVO更新通知?
修改
看起来Transformable类型是一个很好的解决方案,但我仍然想知道更新依赖键路径的正确方法。另一个示例是依赖于fullName
和firstName
的属性lastName
。尽管如此,如果我更改fullName
,我只希望观察者只收到一次而不是两次通知(一次更改firstName
,一次更改lastName
。另一个问题是,在第一次通知时,状态不一致,因为只执行了部分更新。
我认为我原来的例子不够好。怎么样:
@interface Person : NSManagedObject
@property (copy, nonatomic) NSString fullName;
@property (copy, nonatomic) NSString firstName;
@property (copy, nonatomic) NSString lastName;
@end
@implementation Person
@dynamic firstName;
@dynamic lastName;
- (void)setFullName:(NSString *)fullName {
NSArray *names = [fullName componentsSeparatedByString:@" "];
self.firstName = names[0];
self.lastName = names[1];
}
- (CGRect)fullName {
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
+ (NSSet *)keyPathsForValuesAffectingFrame {
return [NSSet setWithObjects:@"firstName", @"lastName", nil];
}
@end
然后,如果我做
person = // get Person object
person.firstName = @"A";
person.lastName = @"B";
// person.fullName is now "A B"
person.fullName = @"C D";
在最后一行,观察者将看到更新的拳头名称(全名为“CB”,但这样的全名根本不存在,这可能会使我的应用程序崩溃)比姓氏更新(“CD”)< / p>
答案 0 :(得分:1)
关于你的第二个问题......你需要更仔细地考虑它。如果您只在firstName上有一个观察者并且更新了fullName,该怎么办?你想要通知观察者吗?
BTW:我认为您应该使用setPrimitiveValue:forKey:
和primitiveValueForKey:
并手动触发通知..
E.g。
- (void)setFullName:(NSString *)fullName {
NSArray *names = [fullName componentsSeparatedByString:@" "];
[self willChangeValueForKey:@"fullName"];
[self setPrimitiveValue:names[0] forKey:@"firstName"];
[self setPrimitiveValue:names[1] forKey:@"lastName"];
[self didChangeValueForKey:@"fullName"];
}
这样,您只会触发fullName的通知,但不会触发firstName和lastName的通知
编辑:好的..关于你的第一个问题(CGRect),如果你不想使用可转换的(即使这是他们常用的一个),你可以“强制”对rect的修改是作为一个整体(你永远不会调用xValue,等等..直接,你可以写你的setFrame为:
- (void)setFrame:(CGRect)frame {
[self willChangeValueForKey:@"frame"];
if (frame.origin.x != self.xValue)
[self setPrimitiveValue:frame.origin.x forKey:@"xValue"];
if (frame.origin.y != self.yValue)
[self setPrimitiveValue:frame.origin.y forKey:@"yValue"];
if (frame.size.width != self.widthValue)
[self setPrimitiveValue:frame.size.width forKey:@"widthValue"];
if (frame.size.height != self.heightValue)
[self setPrimitiveValue:frame.size.height forKey:@"heightValue"];
[self didChangeValueForKey:@"frame"];
}
EDIT2: 关于你的“fullName”问题...我会创建一个只有firstName和lastName的Person对象,然后是一个类别
#import "Person.h"
@interface Person (FullName)
@property (nonatomic, strong) NSString* fullName;
@end
//.m
#import "Person+FullName.h"
@implementation Person (FullName)
- (NSString*)fullName
{
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
- (void)setFullName:(NSString*)fullName
{
NSArray *names = [fullName componentsSeparatedByString:@" "];
[self willChangeValueForKey:@"fullName"];
[self willChangeValueForKey:@"firstName"];
[self willChangeValueForKey:@"lastName"];
[self setPrimitiveValue:names[0] forKey:@"firstName"];
[self setPrimitiveValue:names[1] forKey:@"lastName"];
[self didChangeValueForKey:@"lastName"];
[self didChangeValueForKey:@"firstName"];
[self didChangeValueForKey:@"fullName"];
}
@end