Pertanyaan Obj-C metode mudah untuk mengkonversi dari NSObject dengan properti ke NSDictionary?


Saya berlari melintasi sesuatu yang akhirnya saya temukan, tetapi berpikir mungkin ada cara yang jauh lebih efisien untuk mencapainya.

Saya memiliki objek (NSObject yang mengadopsi protokol MKAnnotation) yang memiliki sejumlah properti (judul, subtitle, latitude, longitude, info, dll.). Saya harus dapat meneruskan objek ini ke objek lain, yang ingin mengekstrak info dari itu menggunakan metode objectForKey, sebagai NSDictionary (karena itulah yang didapatkan dari pengontrol tampilan lain).

Apa yang akhirnya saya lakukan adalah membuat NSMutableDictionary baru dan gunakan setObject: forKey di atasnya untuk mentransfer setiap bagian dari info penting, dan kemudian saya baru saja meneruskan kamus yang baru dibuat.

Apakah ada sebuah lebih mudah cara melakukan ini?

Ini kode yang relevan:

// 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;
    }
}

Terima kasih sebelumnya atas kebijaksanaan kolektif Anda.


Inilah yang saya dapatkan, terima kasih kepada orang-orang, di bawah ...

- (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;
}

    }

Dan perbaikan yang lebih sederhana, seperti yang terlihat di bawah ...

Menggunakan valueForKey bukan objek untuk kunci untuk mengambil informasi.



13
2018-04-25 15:02


asal


Jawaban:


Jika properti memiliki nama yang sama dengan kunci yang digunakan untuk mengakses kamus, maka Anda dapat menggunakan KVC dan memilikinya valueForKey: dari pada objectForKey.

Misalnya diberikan kamus ini

NSDictionary *annotation = [[NSDictionary alloc] initWithObjectsAndKeys:
                             @"A title", @"title", nil];

dan Objek ini

@interface MyAnnotation : NSObject

@property (nonatomic, copy) NSString *title;

@end

tidak masalah jika saya memiliki instance dari kamus atau MyAnnotation Saya bisa menelepon

[annotation valueForKey:@"title"];

Jelas itu bekerja dengan cara lain juga misalnya

[annotation setValue:@"A title" forKey:@"title"];

4
2018-04-25 15:08



Tentu saja! Gunakan objc-runtime dan 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

Dan Anda akan menggunakan seperti ini:

MyObj *obj = [MyObj new];    
NSDictionary *dict = [NSDictionary dictionaryWithPropertiesOfObject:obj];
NSLog(@"%@", dict);

55
2018-04-25 15:19



Ini adalah posting lama dan jawaban Richard J. Ross III benar-benar membantu, tetapi dalam kasus objek kustom (kelas kustom memiliki objek kustom lain di dalamnya). Namun, terkadang properti adalah objek lain dan sebagainya, membuat serialisasi sedikit rumit.

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;

Untuk mengubah jenis objek ini (beberapa objek kustom) ke dalam kamus, saya harus memodifikasi Richard J. Ross III's Answer sedikit.

+(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];
}

Saya berharap ini akan membantu seseorang. Kredit penuh masuk ke Richard J. Ross III.


10
2017-10-15 06:41



Untuk melengkapi metode Richard J. Ross, yang satu ini bekerja dengan NSArray dari objek khusus.

+(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];
}

2
2018-03-07 18:53



Ada begitu banyak solusi dan tidak ada yang berhasil bagi saya karena saya memiliki struktur objek bersarang yang kompleks. Solusi ini mengambil banyak hal dari Richard dan Damien tetapi berimprovisasi karena solusi Damien terkait dengan penamaan kunci sebagai nama kelas.

Ini tajuknya

@interface NSDictionary (PropertiesOfObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj;
@end

Ini adalah file .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

1
2018-02-01 13:05



Anda juga bisa menggunakan NSObject+APObjectMapping kategori yang tersedia di GitHub: https://github.com/aperechnev/APObjectMapping

Ini mudah berhenti. Cukup deskripsikan aturan pemetaan di kelas Anda:

#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

Dan kemudian Anda dapat dengan mudah memetakan objek Anda ke kamus:

MyCustomClass * myObj = [[MyCustomClass alloc] init];
myObj.someNumber = @1;
myObj.someString = @"some string";
NSDictionary * myDict = [myObj mapToDictionary];

Anda juga dapat mem-parse objek Anda dari kamus:

NSDictionary * myDict = @{ @"some_number": @123,
                           @"some_string": @"some string" };
MyCustomClass * myObj = [[MyCustomClass alloc] initWithDictionary:myDict];

0
2018-02-22 13:03