Published on

Objective-C Runtime in Practice 2

Dictionary to Model

Student.h

#import <Foundation/Foundation.h>

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *address;

@end

NSObject+Extension.h

#import <Foundation/Foundation.h>

@interface NSObject (Extension)

+ (instancetype)objectWithDict:(NSDictionary *)dict;

@end

NSObject+Extension.m

#import "NSObject+Extension.h"
#import <objc/runtime.h>

@implementation NSObject (Extension)

+ (instancetype)objectWithDict:(NSDictionary *)dict {
    id obj = [[self alloc] init];

    //    [obj setValuesForKeysWithDictionary:dict];
    NSArray *properties = [self properties];

    // Iterate over the properties array
    for (NSString *key in properties) {
        // Check if the dictionary contains this key
        if (dict[key] != nil) {
            // Use KVC to set the value
            [obj setValue:dict[key] forKeyPath:key];
        }
    }

    return obj;
}

const char *propertiesKey = "propertiesKey";

+ (NSArray *)properties {
    // Check if an associated object already exists; if so, return it directly
    // Parameter 1: the object to associate with
    // Parameter 2: the key for the associated property
    // In OC, a class is essentially an object
    NSArray *plist = objc_getAssociatedObject(self, propertiesKey);
    if(plist != nil) {
        return plist;
    }
    // 1. Get the class's properties
    // Parameters: class and a pointer to the property count; returns an array of all properties
    unsigned int count = 0;// property count pointer
    objc_property_t *list = class_copyPropertyList([self class], &count);// returns an array of all properties
    // 2. Build the property name array
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];
    // 3. Iterate over the array
    for(unsigned int i = 0; i < count; ++i) {
        // Get the property
        objc_property_t pty = list[i];
        // Get the property name
        const char *cname = property_getName(pty);

//        printf("%s",cname);

        [arrayM addObject:[NSString stringWithUTF8String:cname]];
    }
//    printf("");

//    NSLog(@"%@",arrayM);

    free(list);// 4. Must free memory after using class_copyPropertyList

    // 5. Set the associated object
    // Parameter 1: the object to associate with
    // Parameter 2: the key for the associated object
    // Parameter 3: the property value
    // Ownership policy: retain / copy / assign
    objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);

    return arrayM.copy;
}
@end

ViewController.m


- (void)dict2Object {
    Student *stu = [Student objectWithDict:@{@"name" : @"Tom", @"address" : @"Beijing"}];
    NSLog(@"stu的name值为: %@, address为: %@" , stu.name, stu.address);
}

Output:

2016-02-23 18:48:22.022 runtime_demo[34262:4960140] stu的name值为: Tom, address为: Beijing

Associated Objects

Let's jump straight to the code:

MyClass+AssociatedObjectTest.h

#import "MyClass.h"

@interface MyClass (AssociatedObjectTest)

@property (nonatomic, copy) NSString *property;

@end

MyClass+AssociatedObjectTest.m

#import "MyClass+AssociatedObjectTest.h"
#import <objc/runtime.h>

@implementation MyClass (AssociatedObjectTest)

static char MyClassKey;

- (void)setProperty:(NSString *)property {
    [self willChangeValueForKey:@"MyClassKey"];
    objc_setAssociatedObject(self, &MyClassKey,
                             property,
                             OBJC_ASSOCIATION_COPY);
    [self didChangeValueForKey:@"MyClassKey"];
}

- (NSString *)property {
    return objc_getAssociatedObject(self, &MyClassKey);
}

@end

ViewController.m


- (void)testAssociatedObject {
    MyClass *myObj = [[MyClass alloc] init];
    myObj.property = @"Hi! AssociatedObject!";
    NSLog(@"myObj的属性property值为:%@", myObj.property);
}

Output:

2016-02-23 17:46:25.424 runtime_demo[34075:4939529] myObj的属性property值为:Hi! AssociatedObject!

Code

All code from this article can be found on my GitHub runtime_demo .