Objective-C Runtime 一小时入门教程(下)

6.4 苍老师的资料归档篇


苍老师的资料总要整理一下吧!

创建People.h


#import <Foundation/Foundation.h>

 

@interface People : NSObject <NSCoding>

 

@property (nonatomic, copy) NSString *name; // 姓名

@property (nonatomic, strong) NSNumber *age; // 年龄

@property (nonatomic, copy) NSString *occupation; // 职业

@property (nonatomic, copy) NSString *nationality; // 国籍

 

@end


People.m


#import "People.h"

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

@implementation People

 

(void)encodeWithCoder:(NSCoder *)aCoder {

    unsigned int count = 0;

    Ivar *ivars = class_copyIvarList([People class], &count);

    for (NSUInteger i = 0; i < count; i ++) {

        Ivar ivar = ivars[i];

        const char *name = ivar_getName(ivar);

        NSString *key = [NSString stringWithUTF8String:name];

        id value = [self valueForKey:key];

        [aCoder encodeObject:value forKey:key];

    }

    free(ivars);

}

 

(id)initWithCoder:(NSCoder *)aDecoder {

    self = [super init];

    if (self) {

        unsigned int count = 0;

        Ivar *ivars = class_copyIvarList([People class], &count);

        for (NSUInteger i = 0; i < count; i ++) {

            Ivar ivar = ivars[i];

            const char *name = ivar_getName(ivar);

            NSString *key = [NSString stringWithUTF8String:name];

            id value = [aDecoder decodeObjectForKey:key];

            [self setValue:value forKey:key];

        }

        free(ivars);

    }

    return self;

}

 

@end


6.5 苍老师的资料转换篇


服务器返回了大量苍老师的数据,手机端这边接收后如何去转换呢?当然是要将JSON转换为Model啦!

相信平时你们的项目中也用到过这些三方库,下面我们去了解下runtime实现JSON和Model互转。

创建People.h


#import <Foundation/Foundation.h>

 

@interface People : NSObject

 

@property (nonatomic, copy) NSString *name; // 姓名

@property (nonatomic, strong) NSNumber *age; // 年龄

@property (nonatomic, copy) NSString *occupation; // 职业

@property (nonatomic, copy) NSString *nationality; // 国籍

 

// 生成model

(instancetype)initWithDictionary:(NSDictionary *)dictionary;

 

// 转换成字典

(NSDictionary *)covertToDictionary;

 

@end


People.m的代码如下:


#import "People.h"

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

@implementation People

 

(instancetype)initWithDictionary:(NSDictionary *)dictionary

{

    self = [super init];

 

    if (self) {

        for (NSString *key in dictionary.allKeys) {

            id value = dictionary[key];

 

            SEL setter = [self propertySetterByKey:key];

            if (setter) {

                // 这里还可以使用NSInvocation或者method_invoke,不再继续深究了,有兴趣google。

                ((void (*)(id, SEL, id))objc_msgSend)(self, setter, value);

            }

        }

    }

    return self;

}

 

(NSDictionary *)covertToDictionary

{

    unsigned int count = 0;

    objc_property_t *properties = class_copyPropertyList([self class], &count);

 

    if (count != 0) {

        NSMutableDictionary *resultDict = [<a href="http://www.jobbole.com/members/www821839432">@{}</amutableCopy];

 

        for (NSUInteger i = 0; i < count; i ++) {

            const void *propertyName = property_getName(properties[i]);

            NSString *name = [NSString stringWithUTF8String:propertyName];

 

            SEL getter = [self propertyGetterByKey:name];

            if (getter) {

                id value = ((id (*)(id, SEL))objc_msgSend)(self, getter);

                if (value) {

                    resultDict[name] = value;

                } else {

                    resultDict[name] = @"字典的key对应的value不能为nil哦!";

                }

 

            }

        }

 

        free(properties);

 

        return resultDict;

    }

 

    free(properties);

 

    return nil;

}

 

#pragma mark - private methods

 

// 生成setter方法

(SEL)propertySetterByKey:(NSString *)key

{

    // 首字母大写,你懂得

    NSString *propertySetterName = [NSString stringWithFormat:@"set%@:", key.capitalizedString];

 

    SEL setter = NSSelectorFromString(propertySetterName);

    if ([self respondsToSelector:setter]) {

        return setter;

    }

    return nil;

}

 

// 生成getter方法

(SEL)propertyGetterByKey:(NSString *)key

{

    SEL getter = NSSelectorFromString(key);

    if ([self respondsToSelector:getter]) {

        return getter;

    }

    return nil;

}

 

@end


main.m中运行以下代码:


#import <Foundation/Foundation.h>

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

#import "People.h"

 

int main(int argc, const char * argv[]) {

    @autoreleasepool {

 

        NSDictionary *dict = @{

                               @"name" : @"苍井空",

                               @"age"  : @18,

                               @"occupation" : @"老师",

                               @"nationality" : @"日本"

                               };

 

        // 字典转模型

        People *cangTeacher = [[People alloc] initWithDictionary:dict];

        NSLog(@"热烈欢迎,从%@远道而来的%@岁的%@%@",cangTeacher.nationality,cangTeacher.age,cangTeacher.name,cangTeacher.occupation);

 

        // 模型转字典

        NSDictionary *covertedDict = [cangTeacher covertToDictionary];

        NSLog(@"%@",covertedDict);

 

    }

    return 0;

}


最后输出内容如下:




相信通过前面的学习,这些代码不用写过多的注释你也可以看懂了,我把假设是网络返回的苍老师的资料转化为了model,然后又将model转回字典。这是一个JSON转Model相互转换的一个思路,大家稍后运行Demo细细品味。


6.6 苍老师的唱歌篇


这个实例主要是验证一下上文《5.2 消息动态解析》


第一首:


添加sing实例方法,但是不提供方法的实现。验证当找不到方法的实现时,动态添加方法。

创建People.h


#import <Foundation/Foundation.h>

 

@interface People : NSObject

 

@property (nonatomic, copy) NSString *name;

 

(void)sing;

 

@end


创建People.m


#import "People.h"

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

@implementation People

 

(BOOL)resolveInstanceMethod:(SEL)sel

{

    // 我们没有给People类声明sing方法,我们这里动态添加方法

    if ([NSStringFromSelector(sel) isEqualToString:@"sing"]) {

        class_addMethod(self, sel, (IMP)otherSing, "v@:");

        return YES;

    }

    return [super resolveInstanceMethod:sel];

}

 

void otherSing(id self, SEL cmd)

{

    NSLog(@"%@ 唱歌啦!",((People *)self).name);

}


在main.m中运行以下代码:


#import <Foundation/Foundation.h>

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

#import "People.h"

 

int main(int argc, const char * argv[]) {

    @autoreleasepool {

 

        People *cangTeacher = [[People alloc] init];

        cangTeacher.name = @"苍老师";

        [cangTeacher sing];

 

    }

    return 0;

}


结果如下:



我们没有提供苍老师唱歌的方法实现,因此在调用此方法的时候,会调用resolveInstanceMethod方法,我们动态添加了方法。我们也可以返回No,继续向下传递。(此处请返回《5.2 消息动态解析》第一步品味下)


第二首


外面的小鸟在唱歌,但是苍老师的歌声盖过了小鸟,我们只能听到苍老师唱歌了。

这里我们不声明sing方法,将调用途中动态更换调用对象。

在第一首代码的基础上,创建Bird的model

Bird.h

#import  <Foundation/Foundation.h>

 

@interface Bird : NSObject

 

@property (nonatomic, copy) NSString *name;

 

@end


Bird.m


#import "Bird.h"

#import "People.h"

 

@implementation Bird

 

// 第一步:我们不动态添加方法,返回NO,进入第二步;

(BOOL)resolveInstanceMethod:(SEL)sel

{

    return NO;

}

 

// 第二部:我们不指定备选对象响应aSelector,进入第三步;

(id)forwardingTargetForSelector:(SEL)aSelector

{

    return nil;

}

 

// 第三步:返回方法选择器,然后进入第四部;

(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

{

    if ([NSStringFromSelector(aSelector) isEqualToString:@"sing"]) {

        return [NSMethodSignature signatureWithObjCTypes:"v@:"];

    }

 

    return [super methodSignatureForSelector:aSelector];

}

 

// 第四部:这步我们修改调用对象

(void)forwardInvocation:(NSInvocation *)anInvocation

{

    // 我们改变调用对象为People

    People *cangTeacher = [[People alloc] init];

    cangTeacher.name = @"苍老师";

    [anInvocation invokeWithTarget:cangTeacher];

}

 

@end


main.m运行代码如下:


#import <Foundation/Foundation.h>

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

#import "People.h"

#import "Bird.h"

 

int main(int argc, const char * argv[]) {

    @autoreleasepool {

 

        Bird *bird = [[Bird alloc] init];

        bird.name = @"小小鸟";

 

        ((void (*)(id, SEL))objc_msgSend)((id)bird, @selector(sing));

    }

    return 0;

}


运行结果如下:



成功更换了对象,把对象更换为苍老师了。(此处请返回《5.2 消息动态解析》品味)


第三首


苍老师不想唱歌想跳舞了。

这里我是实现不提供声明,不修改调用对象,但是将sing方法修改为dance方法。

创建People.h


#import <Foundation/Foundation.h>

 

@interface People : NSObject

 

@end


People.m


#import "People.h"

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

@implementation People

 

// 第一步:我们不动态添加方法,返回NO,进入第二步;

(BOOL)resolveInstanceMethod:(SEL)sel

{

    return NO;

}

 

// 第二部:我们不指定备选对象响应aSelector,进入第三步;

(id)forwardingTargetForSelector:(SEL)aSelector

{

    return nil;

}

 

// 第三步:返回方法选择器,然后进入第四部;

(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

{

    if ([NSStringFromSelector(aSelector) isEqualToString:@"sing"]) {

        return [NSMethodSignature signatureWithObjCTypes:"v@:"];

    }

 

    return [super methodSignatureForSelector:aSelector];

}

 

// 第四部:这步我们修改调用方法

(void)forwardInvocation:(NSInvocation *)anInvocation

{

    [anInvocation setSelector:@selector(dance)];

    // 这还要指定是哪个对象的方法

    [anInvocation invokeWithTarget:self];

}

 

// 若forwardInvocation没有实现,则会调用此方法

(void)doesNotRecognizeSelector:(SEL)aSelector

{

    NSLog(@"消息无法处理:%@", NSStringFromSelector(aSelector));

}

 

(void)dance

{

    NSLog(@"跳舞!!!come on!");

}

 

@end


在main.m中运行如下代码:


#import <Foundation/Foundation.h>

 

#if TARGET_IPHONE_SIMULATOR

#import <objc/objc-runtime.h>

#else

#import <objc/runtime.h>

#import <objc/message.h>

#endif

 

#import "People.h"

 

int main(int argc, const char * argv[]) {

    @autoreleasepool {

 

        People *cangTeacher = [[People alloc] init];

 

        ((void(*)(id, SEL)) objc_msgSend)((id)cangTeacher, @selector(sing));

 

    }

    return 0;

}


结果如图:


成功更换了方法,苍老师由唱歌改为跳舞了(此处请返回《5.2 消息动态解析》品味)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值