在 CoreData 中存储 NSMutableAttributedString 的简单方法
Posted
技术标签:
【中文标题】在 CoreData 中存储 NSMutableAttributedString 的简单方法【英文标题】:Simple way to store NSMutableAttributedString in CoreData 【发布时间】:2013-12-18 02:03:55 【问题描述】:我正在尝试将NSMutableAttributedString
存储在CoreData
中,但我遇到了问题,因为我的NSMutableAttributedString
的某些属性包含无法归档的Core Foundation 对象。有没有一种简单的方法可以让这个对象存储在 CoreData 中,而不必手动做一些乱七八糟的事情?
【问题讨论】:
您建议如何存储无法归档的内容? @DuncanGroenewald - 这就是我要问的,猜这是不可能的? 您在属性中包含的具体内容是什么?您不能将它们子类化并将归档添加到您自己的子类中吗? @DuncanGroenewald - 它是核心基础的东西,如 CGColorRef、CTFontRef、CTParagraphStyleRef。我发现我必须创建自己的子类并将所有这些值转换为 UIKit。猜猜这真的不是一个简单的方法。 嗯,你不能使用标准属性来实现同样的事情吗? 【参考方案1】:NSMutableAttributedString
符合NSCoding
,这意味着它知道如何将自己转换为NSData
,并通过Core Data 知道如何使用的协议来实现。
使属性“可转换”,然后将属性字符串分配给它。由于它是可转换的,Core Data 将在您分配值时使用NSCoding
将其转换为NSData
,并在您读取它时将其转换回属性字符串。
请注意,您将无法使用谓词来过滤此字段的结果。但是存储和检索它很简单。
【讨论】:
它确实符合 NSCoding,但它的属性可能不符合(属性)。【参考方案2】:虽然上面的答案是对的,但它有一个很大的缺点:
无法构建查询 NSAttributedString 对象内容的获取请求/谓词!
这样的谓词在执行时会引发异常:
[NSPredicate predicateWithFormat:@"(content CONTAINS[cd] %@)", @"test"]];
要在 Core Data 中存储一个“可获取”的 NSAttributedString,需要将 NSAttributedString 分成两部分:一个 NSString 端(可以获取)和一个 NSData 端,它存储属性。
这种拆分可以通过在Core Data实体中创建三个属性来实现:
-
影子 NSString 属性 ('contentString')
影子 NSData 属性('contentAttributes')
“未定义”瞬态属性(“内容”)
在自定义实体类中,从其阴影创建的“内容”属性和对属性的更改也反映到其阴影。
头文件:
/**
MMTopic
*/
@interface MMTopic : _MMTopic
@property (strong, nonatomic) NSAttributedString* content;
@end
实现文件:
/**
MMTopic (PrimitiveAccessors)
*/
@interface MMTopic (PrimitiveAccessors)
- (NSAttributedString *)primitiveContent;
- (void)setPrimitiveContent:(NSAttributedString *)pContent;
@end
/**
MMTopic
*/
@implementation MMTopic
static NSString const* kAttributesDictionaryKey = @"AttributesDictionary";
static NSString const* kAttributesRangeKey = @"AttributesRange";
/*
awakeFromFetch
*/
- (void)awakeFromFetch
[super awakeFromFetch];
// Build 'content' from its shadows 'contentString' and 'contentAttributes'
NSString* string = self.contentString;
NSMutableAttributedString* content = [[NSMutableAttributedString alloc] initWithString:string];
NSData* attributesData = self.contentAttributes;
NSArray* attributesArray = nil;
if (attributesData)
NSKeyedUnarchiver* decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:attributesData];
attributesArray = [[NSArray alloc] initWithCoder:decoder];
if ((content) &&
(attributesArray.count))
for (NSDictionary* attributesDictionary in attributesArray)
//NSLog(@"%@: %@", NSStringFromRange(((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue), attributesDictionary[kAttributesDictionaryKey]);
[content addAttributes:attributesDictionary[kAttributesDictionaryKey]
range:((NSValue*)attributesDictionary[kAttributesRangeKey]).rangeValue];
[self setPrimitiveContent:content];
/*
content
*/
@dynamic content;
/*
content (getter)
*/
- (NSAttributedString *)content
[self willAccessValueForKey:@"content"];
NSAttributedString* content = [self primitiveContent];
[self didAccessValueForKey:@"content"];
return content;
/*
content (setter)
*/
- (void)setContent:(NSAttributedString *)pContent
[self willChangeValueForKey:@"content"];
[self setPrimitiveValue:pContent forKey:@"content"];
[self didChangeValueForKey:@"content"];
// Update the shadows
// contentString
[self setValue:pContent.string
forKey:@"contentString"];
// contentAttributes
NSMutableData* data = [NSMutableData data];
NSKeyedArchiver* coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
NSMutableArray* attributesArray = [NSMutableArray array];
[pContent enumerateAttributesInRange:NSMakeRange(0, pContent.length)
options:0
usingBlock:^(NSDictionary* pAttributesDictionary, NSRange pRange, BOOL* prStop)
//NSLog(@"%@: %@", NSStringFromRange(pRange), pAttributesDictionary);
[attributesArray addObject:@
kAttributesDictionaryKey: pAttributesDictionary,
kAttributesRangeKey: [NSValue valueWithRange:pRange],
];
];
[attributesArray encodeWithCoder:coder];
[coder finishEncoding];
[self setValue:data
forKey:@"contentAttributes"];
@end
现在可以通过以下方式获取:
[NSPredicate predicateWithFormat:@"(contentString CONTAINS[cd] %@)", @"test"]];
虽然对 NSAttributedString 的任何访问都是这样的:
textField.attributedText = pTopic.content;
在 Core Data 中使用“非标准属性”的规则记录在此:Apple docs
【讨论】:
【参考方案3】:好吧,我不确定您要对属性字符串做什么,但如果它是格式化文本,那么您不能使用 NSFont 等。
看这里http://ossh.com.au/design-and-technology/software-development,我发布了一些关于使用 uitextview 和 nstextview 格式化样式和图像的内容,但主要是关于属性字符串。
这些东西都存储在核心数据中。
【讨论】:
【参考方案4】:我在 ios5 出来时开始使用CoreText
,因此使用 Core Foundation 值作为属性。但是我现在意识到,自从iOS6出来后,我现在可以在属性字典中使用NSForegroundColorAttributeName
、NSParagraphStyleAttributeName
、NSFontAttributeName
等,并且这些键伴随着UIColor
、NSMutableParagraphStyle
等对象UIFont
可以存档。
【讨论】:
以上是关于在 CoreData 中存储 NSMutableAttributedString 的简单方法的主要内容,如果未能解决你的问题,请参考以下文章
为啥 NSDate() 在 CoreData 中存储一个奇怪的值