从具有属性的 NSObject 转换为 NSDictionary 的 Obj-C 简单方法?
Posted
技术标签:
【中文标题】从具有属性的 NSObject 转换为 NSDictionary 的 Obj-C 简单方法?【英文标题】:Obj-C easy method to convert from NSObject with properties to NSDictionary? 【发布时间】:2012-04-25 15:02:31 【问题描述】:我遇到了一些我最终想通的东西,但认为可能有一种更有效的方法来完成它。
我有一个对象(一个采用 MKAnnotation 协议的 NSObject),它具有许多属性(标题、副标题、纬度、经度、信息等)。我需要能够将此对象传递给另一个对象,该对象希望使用 objectForKey 方法从中提取信息,作为 NSDictionary(因为这是从另一个视图控制器获得的)。
我最终做的是创建一个新的 NSMutableDictionary 并在其上使用 setObject: forKey 来传输每条重要信息,然后我只是传递了新创建的字典。
有没有一种更简单的方法来做到这一点?
以下是相关代码:
// sender contains a custom map annotation that has extra properties...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
if ([[segue identifier] isEqualToString:@"showDetailFromMap"])
DetailViewController *dest =[segue destinationViewController];
//make a dictionary from annotaion to pass info
NSMutableDictionary *myValues =[[NSMutableDictionary alloc] init];
//fill with the relevant info
[myValues setObject:[sender title] forKey:@"title"] ;
[myValues setObject:[sender subtitle] forKey:@"subtitle"];
[myValues setObject:[sender info] forKey:@"info"];
[myValues setObject:[sender pic] forKey:@"pic"];
[myValues setObject:[sender latitude] forKey:@"latitude"];
[myValues setObject:[sender longitude] forKey:@"longitude"];
//pass values
dest.curLoc = myValues;
提前感谢您的集体智慧。
这是我想出的,感谢下面的人......
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
if ([[segue identifier] isEqualToString:@"showDetailFromMap"])
DetailViewController *dest =[segue destinationViewController];
NSArray *myKeys = [NSArray arrayWithObjects:
@"title",@"subtitle",@"info",@"pic",@"latitude",@"longitude", nil];
//make a dictionary from annotaion to pass info
NSDictionary *myValues =[sender dictionaryWithValuesForKeys:myKeys];
//pass values
dest.curLoc = myValues;
还有一个更简单的修复方法,如下所示...
使用 valueForKey 而不是 object for key 来检索信息。
【问题讨论】:
看到这个答案:***.com/a/47726648/4132714 【参考方案1】:当然!使用 objc-runtime 和 KVC!
#import <objc/runtime.h>
@interface NSDictionary(dictionaryWithObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id) obj;
@end
@implementation NSDictionary(dictionaryWithObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++)
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
[dict setObject:[obj valueForKey:key] forKey:key];
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
@end
你会这样使用:
MyObj *obj = [MyObj new];
NSDictionary *dict = [NSDictionary dictionaryWithPropertiesOfObject:obj];
NSLog(@"%@", dict);
【讨论】:
是的,objc-runtime 绝对是要走的路。 您也可以从运行时获取密钥并使用来自NSKeyValueCoding
的-dictionaryWithValuesForKeys:
。或者您直接将其与硬编码的键列表一起使用,因为您可能不希望字典中的 every 属性。
非常感谢。这就像一个魅力。请参阅上面我更正的问题。
@khaled 不,不应该。公认的答案是指对 OP 帮助最大的答案,而不是对每个人最有帮助的答案。
记住要注意 nil 值。因为你不能在字典中插入 nil。【参考方案2】:
这是一篇旧帖子,Richard J. Ross III 的回答确实很有帮助,但在自定义对象的情况下(自定义类中有另一个自定义对象)。但是,有时属性是其他对象等等,使得序列化有点复杂。
Details * details = [[Details alloc] init];
details.tomato = @"Tomato 1";
details.potato = @"Potato 1";
details.mangoCount = [NSNumber numberWithInt:12];
Person * person = [[Person alloc]init];
person.name = @"HS";
person.age = @"126 Years";
person.gender = @"?";
person.details = details;
为了将这些类型的对象(多个自定义对象)转换为字典,我不得不稍微修改一下 Richard J. Ross III's Answer。
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++)
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
Class classObject = NSClassFromString([key capitalizedString]);
if (classObject)
id subObj = [self dictionaryWithPropertiesOfObject:[obj valueForKey:key]];
[dict setObject:subObj forKey:key];
else
id value = [obj valueForKey:key];
if(value) [dict setObject:value forKey:key];
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
我希望它会对某人有所帮助。完全归功于 Richard J. Ross III。
【讨论】:
嗯,很有趣。请注意,对于相互依赖的条目,这也将失败(例如,对象a
引用对象b
谁引用对象a
,等等),。在那种情况下,我会改用NSCoder
/ NSCoding
,因为这并不是它的真正用途。
但是如果你有一个“classObject”的 NSArray,它是行不通的。
很好的答案,对我有用!我唯一需要做的小改动是在大写字符串部分:我的类名是 UserDevice,所以我必须更改方法,只将第一个字母大写。【参考方案3】:
如果属性与用于访问字典的键名称相同,那么您可以只使用 KVC 并使用 valueForKey:
而不是 objectForKey
。
例如给定这本词典
NSDictionary *annotation = [[NSDictionary alloc] initWithObjectsAndKeys:
@"A title", @"title", nil];
还有这个对象
@interface MyAnnotation : NSObject
@property (nonatomic, copy) NSString *title;
@end
如果我有字典的实例或MyAnnotation
我可以调用也没关系
[annotation valueForKey:@"title"];
显然这也适用于其他方式,例如
[annotation setValue:@"A title" forKey:@"title"];
【讨论】:
很高兴知道这一点。是的,我将属性命名为相同。我收到了非字典不会响应 objectForKey 的错误,但没有意识到 valueForKey 是这样工作的。谢谢!【参考方案4】:为了完成Richard J. Ross的方法,这个是和自定义对象的NSArray一起工作的。
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++)
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
Class classObject = NSClassFromString([key capitalizedString]);
id object = [obj valueForKey:key];
if (classObject)
id subObj = [self dictionaryWithPropertiesOfObject:object];
[dict setObject:subObj forKey:key];
else if([object isKindOfClass:[NSArray class]])
NSMutableArray *subObj = [NSMutableArray array];
for (id o in object)
[subObj addObject:[self dictionaryWithPropertiesOfObject:o] ];
[dict setObject:subObj forKey:key];
else
if(object) [dict setObject:object forKey:key];
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
【讨论】:
【参考方案5】:有这么多的解决方案,但没有什么对我有用,因为我有一个复杂的嵌套对象结构。这个解决方案从 Richard 和 Damien 那里得到了一些东西,但即兴发挥了 Damien 的解决方案与将键命名为类名有关。
这是标题
@interface NSDictionary (PropertiesOfObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj;
@end
这是.m文件
@implementation NSDictionary (PropertiesOfObject)
static NSDateFormatter *reverseFormatter;
+ (NSDateFormatter *)getReverseDateFormatter
if (!reverseFormatter)
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
reverseFormatter = [[NSDateFormatter alloc] init];
[reverseFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
[reverseFormatter setLocale:locale];
return reverseFormatter;
+ (NSDictionary *)dictionaryWithPropertiesOfObject:(id)obj
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++)
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
id object = [obj valueForKey:key];
if (object)
if ([object isKindOfClass:[NSArray class]])
NSMutableArray *subObj = [NSMutableArray array];
for (id o in object)
[subObj addObject:[self dictionaryWithPropertiesOfObject:o]];
dict[key] = subObj;
else if ([object isKindOfClass:[NSString class]])
dict[key] = object;
else if ([object isKindOfClass:[NSDate class]])
dict[key] = [[NSDictionary getReverseDateFormatter] stringFromDate:(NSDate *) object];
else if ([object isKindOfClass:[NSNumber class]])
dict[key] = object;
else if ([[object class] isSubclassOfClass:[NSObject class]])
dict[key] = [self dictionaryWithPropertiesOfObject:object];
return dict;
@end
【讨论】:
【参考方案6】:您也可以使用 GitHub 上提供的 NSObject+APObjectMapping
类别:https://github.com/aperechnev/APObjectMapping
这很容易。只需描述您班级中的映射规则即可:
#import <Foundation/Foundation.h>
#import "NSObject+APObjectMapping.h"
@interface MyCustomClass : NSObject
@property (nonatomic, strong) NSNumber * someNumber;
@property (nonatomic, strong) NSString * someString;
@end
@implementation MyCustomClass
+ (NSMutableDictionary *)objectMapping
NSMutableDictionary * mapping = [super objectMapping];
if (mapping)
NSDictionary * objectMapping = @ @"someNumber": @"some_number",
@"someString": @"some_string" ;
return mapping
@end
然后您可以轻松地将您的对象映射到字典:
MyCustomClass * myObj = [[MyCustomClass alloc] init];
myObj.someNumber = @1;
myObj.someString = @"some string";
NSDictionary * myDict = [myObj mapToDictionary];
你也可以从字典中解析你的对象:
NSDictionary * myDict = @ @"some_number": @123,
@"some_string": @"some string" ;
MyCustomClass * myObj = [[MyCustomClass alloc] initWithDictionary:myDict];
【讨论】:
以上是关于从具有属性的 NSObject 转换为 NSDictionary 的 Obj-C 简单方法?的主要内容,如果未能解决你的问题,请参考以下文章
如果将一个NSObject从NSDictionary转换为NSString?
使用 Monotouch 时如何将 CGColor 转换为 NSObject?