Runtime梳理(二)

挖就挖底层.png

继上《Runtime梳理(一)》

这一章分成四个部分进行学习:1. Runtime的特征 2. Runtime的Category 3.Runtime的归档 功能 4. Runtime的数据转换

第一部分:Runtime的特征

直接举例:

  • Gril类

// "Gril.h"
@interface Gril : NSObject
{
NSString * _occupation;
NSString * _nationality;
}
@property (copy, nonatomic) NSString * name;
@property (assign, nonatomic) NSUInteger age;

  • (NSDictionary *)allProperties;
  • (NSDictionary *)allvars;
  • (NSDictionary *)allmethods;

* ----
> 

// Gril.m

if TARGET_IPHONE_SIMULATOR

import <objc/objc-runtime.h>

else

import <objc/runtime.h>

import <objc/message.h>

endif

import "Gril.h"

@interface Gril () //
{
NSString * _liver;
}
@property (assign, nonatomic) double heigth;
@end


@implementation Gril
{
int _countA; // 注意这里哦
}

  • (NSDictionary *)allProperties
    {
    unsigned int count = 0;
    objc_property_t * properties = class_copyPropertyList([self class], &count);
    NSMutableDictionary * dict = [@{} mutableCopy];
    for (NSUInteger i = 0; i < count; i++)
    {
    // 名称
    const char * propertyName = property_getName(properties[i]);
    NSString *name = [NSString stringWithUTF8String:propertyName];
    // 值
    id propertyValue = [self valueForKey:name];
    if (propertyValue) {
    dict[name] = propertyValue;
    }
    else
    {
    dict[name] = @"!waning; key:value != nil------allProperties";
    }
    }
    free(properties); // 去内存
    return dict;
    }

* ----

  • (NSDictionary *)allvars
    {
    unsigned int count = 0;
    NSMutableDictionary * dict = [@{} mutableCopy];
    Ivar * ivars = class_copyIvarList([self class], &count);
    for (NSUInteger i = 0; i < count; i++)
    {
    const char * varName = ivar_getName(ivars[i]);
    NSString *name = [NSString stringWithUTF8String:varName];
    id varValue = [self valueForKey:name];
    if (varValue) {
    dict[name] = varValue;
    }
    else
    {
    dict[name] = @"!waning; key:value != nil------allvars";
    }
    }
    free(ivars);
    return dict;
    }

* ----

  • (NSDictionary *)allmethods
    {
    unsigned int count = 0;
    NSMutableDictionary * dict = [@{} mutableCopy];
    Method * methods = class_copyMethodList([self class], &count);
    for (NSUInteger i = 0; i < count; i++)
    {
    SEL methodSEL = method_getName(methods[i]);
    const char * methodName = sel_getName(methodSEL);
    NSString * name = [NSString stringWithUTF8String:methodName];
    int arguments = method_getNumberOfArguments(methods[i]);
    dict[name] = @(arguments-2);
    }
    free(methods);
    return dict;
    }

> 大家看到什么?.m文件中
变量什么 私有变量? 匿名扩展相关知识自己补充了,这里不赘述了

我们来验证一下我们获得了什么吧?
> ````
// 首先 添加对象
    Gril * grilTeacher = [[Gril alloc] init];
    grilTeacher.name = @"女教师";
    grilTeacher.age = 26;
    [grilTeacher setValue:@"教师" forKey:@"occupation"];
  1. 下面获得property
 NSDictionary * dict = [grilTeacher allProperties];
    NSLog(@"%@", dict);

打印结果:
2016-04-09 19:55:50.614 RuntimeLine[2305:195629] { age = 26; heigth = 0; //这个是.m文件属性哦,获取了。。。 name = "\U5973\U6559\U5e08"; }
heigth? 不意外吗?!

  1. 获得Ivar
NSDictionary * dict = [grilTeacher allvars];
    NSLog(@"%@", dict);

打印结果:
2016-04-09 20:01:41.397 RuntimeLine[2349:199801] { "_age" = 26; "_countA" = 0;//这是....获取了。。。 "_heigth" = 0; "_liver" = "!waning\Uff1b key:value != nil------allvars"; "_name" = "\U5973\U6559\U5e08"; "_nationality" = "!waning\Uff1b key:value != nil------allvars"; "_occupation" = "\U6559\U5e08"; }
countA 也获取了,还不意外?!
还顺便理解了propertyIvar区别吧

  1. 获取Methods
    NSDictionary * dict = [grilTeacher allmethods];
    NSLog(@"%@", dict);

打印结果:
2016-04-09 20:09:39.363 RuntimeLine[2372:204392] { ".cxx_destruct" = 0;// 这个方法这在ARC的环境下才会被调用 销毁意思 age = 0; allProperties = 0; //++ allmethods = 0; //++ allvars = 0; --- heigth = 0; name = 0; "setAge:" = 1; ???? "setHeigth:" = 1; ??? "setName:" = 1; ??? }
很牛逼吧,set get方法 内存管理机制 ???还不很意外了吗???!

不能做的都做了, 万能吧,还真是,

这就是Runtime?Objective-C objc 的根基所在


第二部分:Runtime的Category

  • 创建Gril+Hiee

// Gril+Hiee.h

import "Gril.h"

typedef void (^codingCallBack)();
@interface Gril (Hiee)
@property (strong, nonatomic) NSNumber * associatedBust; //x胸围??呵呵
@property (copy, nonatomic) codingCallBack associatedCallBack; // 写代码 哈哈
@end


> ````
// Gril+Hiee.m
#import "Gril+Hiee.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation Gril (Hiee)
- (void)setAssociatedBust:(NSNumber *)bust
{
    // 关联对象 objc_association_retain_nonatomc
    objc_setAssociatedObject(self, @selector(associatedBust), bust, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSNumber *)associatedBust
{
    // 获取关联
    return objc_getAssociatedObject(self, @selector(associatedBust));
}
- (void)setAssociatedCallBack:(codingCallBack)callBack
{
    objc_setAssociatedObject(self, @selector(associatedCallBack), callBack, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (codingCallBack)associatedCallBack
{
    return objc_getAssociatedObject(self, @selector(associatedCallBack));
}

验证一下在category中是否有效???
准备:

Gril * grilTeacher = [[Gril alloc] init];
grilTeacher.name = @"女教师";
grilTeacher.age = 26;
[grilTeacher setValue:@"教师" forKey:@"occupation"];
grilTeacher.associatedBust = @(120); // 豪大大的
grilTeacher.associatedCallBack = ^(){
NSLog(@"当男友出轨的时候,赶紧回来找bug,一定是代码没写好,导致的。。。。");
};
grilTeacher.associatedCallBack();
NSDictionary * propertyDict = [grilTeacher allProperties];
NSDictionary * lvarDict = [grilTeacher allvars];
NSDictionary * methodDict = [grilTeacher allmethods];
NSLog(@"%@", propertyDict);
NSLog(@"%@", lvarDict);
NSLog(@"%@", methodDict);


输出结果呢?
`
2016-04-09 21:22:50.685 RuntimeLine[2959:242125] 当男友出轨的时候,赶紧回来找bug,一定是代码没写好,导致的。。。。
2016-04-09 21:22:50.687 RuntimeLine[2959:242125] {
    age = 26;
    associatedBust = 120;  、、、、???有效
    associatedCallBack = "<__NSGlobalBlock__: 0x1000030e0>"; 、、、、???有效
    heigth = 0;
    name = "\U5973\U6559\U5e08";
}
2016-04-09 21:22:50.688 RuntimeLine[2959:242125] {
    "_age" = 26;
    "_countA" = 0;
    "_heigth" = 0;
    "_liver" = "!waning\Uff1b key:value != nil------allvars";
    "_name" = "\U5973\U6559\U5e08";
    "_nationality" = "!waning\Uff1b key:value != nil------allvars";
    "_occupation" = "\U6559\U5e08";
}
2016-04-09 21:22:50.688 RuntimeLine[2959:242125] {
    ".cxx_destruct" = 0;
    age = 0;
    allProperties = 0;
    allmethods = 0;
    allvars = 0;
    associatedBust = 0;
    associatedCallBack = 0;
    heigth = 0;
    name = 0;
    "setAge:" = 1;
    "setAssociatedBust:" = 1; 、、、、、???有效
    "setAssociatedCallBack:" = 1; 、、、、、???有效
    "setHeigth:" = 1;
    "setName:" = 1;
}
Program ended with exit code: 0
`
看到了吗? 在`category`中也完全有效? 有点废话?还有哦

###第三部分: Runtime的归档功能
> 大家知道:
1. 为了将应用数据存储到硬盘中,iOS提供基本的文件`API`、`Property List`序列化、`SQLite`、`CoreData`以及`NSCoding`。
2. 是类对象本身数据的写入到本地文件。
我 们需要实现两个方法: `encodeWithCoder`和`initWithEncoder`。`encodeWithCoder`就是编码,`initWithCoder`就是解码。 `encodeWithCoder`方法传入的是一个`NSCoder`对象,实现的时候我们就可以调用`encodeObject、encodeFloat、 encodeInt`等各种方法并通过指定键值进行编码。

* 下面我们写专业一点 归档嘛
> ````
// Gril.h
#import <Foundation/Foundation.h>
@interface Gril : NSObject <NSCoding>
@property (copy, nonatomic) NSString * occupation; // 专业
@property (copy, nonatomic) NSString * nationality;  // 国度
@property (copy, nonatomic) NSString * name;  // 名称
@property (assign, nonatomic) NSUInteger age;  // 年龄
@end

// Gril.m

if TARGET_IPHONE_SIMULATOR

import <objc/objc-runtime.h>

else

import <objc/runtime.h>

import <objc/message.h>

endif

import "Gril.h"

@implementation Gril

  • (void)encodeWithCoder:(NSCoder *)aCoder
    {
    unsigned int count = 0;
    Ivar * ivars = class_copyIvarList([self class], &count);
    for (NSUInteger i = 0; i < count; i++)
    {
    const char * name = ivar_getName(ivars[i]);
    NSString * key = [NSString stringWithUTF8String:name];
    id value = [self valueForKey:key];
    [aCoder encodeObject:value forKey:key];
    }
    }
*----
  • (instancetype)initWithCoder:(NSCoder *)aDecoder
    {
    if (self = [super init]) {
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (NSUInteger i = 0; i < count; i ++) {
    const char *name = ivar_getName(ivars[i]);
    NSString *key = [NSString stringWithUTF8String:name];
    id value = [aDecoder decodeObjectForKey:key];
    [self setValue:value forKey:key];
    }
    free(ivars);
    }
    return self;
    }
呵呵!!这样就完成了。。。。

其实还有的...
###第四部分: runtime的数据转换
> 反之,当服务器返回了大量数据,iOS端这边接收后如何去转换呢?在我们的项目中也用到过这些`json`转成Model三方库:MJExtention、YY。。。忘了,下面了解下runtime实现JSON和Model互转。
* 还是用我们类Gril吧, 我就喜欢用Gril来表达一下

> ````
// Gril.h
#import <Foundation/Foundation.h>
@interface Gril : NSObject 
@property (copy, nonatomic) NSString * occupation; // 专业
@property (copy, nonatomic) NSString * nationality;  // 国度
@property (copy, nonatomic) NSString * name;  // 名称
@property (assign, nonatomic) NSUInteger age;  // 年龄
@end

// Gril.m

if TARGET_IPHONE_SIMULATOR

import <objc/objc-runtime.h>

else

import <objc/runtime.h>

import <objc/message.h>

endif

import "Gril.h"

@implementation Gril
/** 生成模型 */

  • (instancetype)initWithDictionary:(NSDictionary )dict
    {
    if (self = [super init]) {
    for (NSString * key in dict.allKeys) {
    id value = dict[key];
    SEL setter = [self propertySetterByKey:key];
    if (setter) {
    ((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 *dict = [@{} mutableCopy];
        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)
                {
                    dict[name] = value;
                }
                else
                {
                    dict[name] = @"!Waning: key--value != nil !";
                }
            }
        }
        free(properties);
        return dict;
    }
    free(properties);
    return nil;
}

*----

pragma mark -- private methods

/** setter方法 */

  • (SEL)propertySetterByKey:(NSString )key
    {
    // 首字母大写 setAge
    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;
    }

* 测试验证:

> ````
NSDictionary *dict = @{
                           @"name" : @"萌萌",
                           @"age"  : @(26),
                           @"occupation" : @"教师",
                           @"nationality" : @"瑞典国"
                           };
    // 字典转模型
    Gril * grilTeacher = [[Gril alloc] initWithDictionary:dict];
    NSLog(@"age:%d, name: %@, occupation: %@, nationality:%@",  (int)grilTeacher.age, grilTeacher.name, grilTeacher.occupation, grilTeacher.nationality);
    // 模型转字典
    NSDictionary * modelDict = [grilTeacher covertToDictionary];
    NSLog(@"%@",modelDict);

我们来看一下打印的结果是否成功吧:

`
2016-04-10 01:25:38.836 RuntimeLine[4531:354370] age:6695, name: 萌萌, occupation: 教师, nationality:瑞典国
2016-04-10 01:25:38.837 RuntimeLine[4531:354370] {
age = 26;
associatedBust = "!Waning: key--value != nil !";
associatedCallBack = "!Waning: key--value != nil !";

name = "\U840c\U840c";
nationality = "\U745e\U5178\U56fd";
occupation = "\U6559\U5e08";

}
`
呵呵, 又成功了,

  • 案例小结:

看看我们都干了什么?!What have we done?!!!
自己总结。。。

回顾一下:一共学了四个部分:1. Runtime的特征 2. Runtime的Category 3.Runtime的归档 功能 4. Runtime的数据转换

下一篇继续: 《Runtime梳理(三)》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容