运行时相关

  • 在CYPerson.h文件中
#import <Foundation/Foundation.h>


@interface CYPerson : NSObject
{
    int _money;
}
@property (nonatomic, assign, readonly) int age;
@property (nonatomic, assign) double height;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSArray *books;
@property (nonatomic, strong) id test;
@property (nonatomic, assign) CGRect rect;
@property (nonatomic, copy) void (^block)();
@property (nonatomic, assign) int *p;
@property (nonatomic, strong) CYCat *cat;
@end
  • 在CYPerson.m中
#import "CYPerson.h"

@implementation CYPerson

@end
  • 在main.m文件中
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "CYPerson.h"

void getIvars()
{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([CYPerson class], &count);
    // class_copyIvarList获取成员变量列表,它返回的是一个指针指向的是最前面的那一个
    // ([CYPerson class], &count)这里面放的是类,你把类给我,我就获取里面所有的成员变量。&count放的是成员变量的个数
    for (int i = 0; i < count; i++) {
        // for循环把所有成员变量遍历出来
        Ivar ivar = ivars[i]; // *(ivars + i)
        // 取出每一个成员变量Ivar,而且Ivar是以数组的形式去访问
        // ivars[i]的写法相当于*(ivars + i)---跳到下一个,取出这个指针指向的东西
        NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
       // %s是因为这里是C语言的东西
       // ivar_getName(ivar)这是去获取所有成员变量的名字,这样就挖掘到了所有成员变量
       // ivar_getTypeEncoding(ivar)表示获取一个成员变量的类型字符串
       // 而且今后如果你有要获取所有什么double,int类型的成员变量,这种方式,一打印就出来了
    }

    free(ivars);
    // 这里是通过copy出来的,所以需要释放的
}

// 这里获取所有属性,和上面一样
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        unsigned int count = 0;
        objc_property_t *properties = class_copyPropertyList([CYPerson class], &count);
        // class_copyPropertyList获取属性列表
        // class_copyProtocolList([CYPerson class], &count);是获取协议列表,这样可以获取这个类遵循的所有协议
        // class_copyMethodList([CYPerson class], &count);是获取方法列表,这样可以知道这个类遵循 那些方法
        for (int i = 0; i < count; i++) {
            objc_property_t property = properties[i];
            NSLog(@"%s   ----     %s", property_getName(property), property_getAttributes(property));
       // property_getName(property)这是去获取所有属性的名字
       // property_getAttributes(property)获取所有属性的类型
        }

        free(properties);

//        method_exchangeImplementations(<#Method m1#>, <#Method m2#>)
//        方法交换---这个方法的意义在于我可以将苹果系统内部的方法替换成我自己的方法。当你认为系统自带的方法不好用时,你就将它替换为自己的方法
    }
    return 0;
}

*


*

*


*

*


*

  • 运行时还可以动态添加成员变量方法,动态添加方法,动态添加属性,动态添加协议这四个方法
  • 假如说在代码里面你只写了如上几个属性,但是在我动态添加后,在程序运行过程中会无缘无故多一个属性出来,但是你在代码中是看不见的。这就是运行时能干的事情,相当于操纵你的内存,但是这也仅仅是运行时的冰山一角

*


*

        method_exchangeImplementations(<#Method m1#>, <#Method m2#>)
//      方法交换---这个方法的意义在于我可以将苹果系统内部的方法替换成我自己的方法。当你认为系统自带的方法不好用时,你就将它替换为自己的方法





  • RunTime
    • 1.获取内部所有属性和成员变量
    • 2.Method Swizzle--iOS黑魔法
      • 直观用途:交换方法实现
  • 在CYPerson.h文件中
#import <Foundation/Foundation.h>

@interface CYPerson : NSObject
- (void)run;
- (void)study;
@end
  • 在CYPerson.m文件中
#import "CYPerson.h"
@implementation CYPerson

- (void)run
{
    NSLog(@"%s", __func__);
}

- (void)study
{
    NSLog(@"%s", __func__);
}

@end
#import "ViewController.h"
#import "CYPerson.h"
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

        Method method1 = class_getInstanceMethod([CYPerson class], @selector(run));
        Method method2 = class_getInstanceMethod([CYPerson class], @selector(study));
        method_exchangeImplementations(method1, method2);


        CYPerson *p = [[CYPerson alloc] init];
        [p run];

        [p study];
  • 可以看出来:我调用run,它执行的study。我调用study,它执行的run

  • 那这个有啥用呢?
    • 在这里看出来是没什么卵用,但是它的价值就在于可以交换系统自带的方法
    • 比如说我可以让控制器的viewDidLoad方法换成我自己的viewDidLoad方法,让它执行的时候执行到我自己的方法中去了。
    • 下面举个例子:
      • 我想监控控制器什么时候死亡,那我就得重写dealloc方法。那么假如我们的项目中有30个或者更多的控制器,我想看它死掉没有,防止循环引用,那我是不是20个都得实现dealloc方法?那我何不再搞一个方法出来,比如说CY_dealloc:方法。你本来想执行dealloc的,现在执行到我CY_dealloc:方法里去。那我就可以在这里统一监控所有控制器的死亡
      • 下面我就做一下这个事情。为了保证所有控制器都有,我弄一个控制器的分类--UIViewController+CYExtention
#import <UIKit/UIKit.h>
#import <objc/runtime.h>

@implementation UIViewController (CYExtension)
+ (void)load
{
    Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
    Method method2 = class_getInstanceMethod(self, @selector(CY_dealloc));
    method_exchangeImplementations(method1, method2);
}

- (void)CY_dealloc
{
    NSLog(@"%@ - CY_dealloc", self);
}
@end
  • 这样就让所有控制器的dealloc来到了我CY_dealloc里面,我可以统一监控所有控制器
    • 上面我没有导入头文件,是因为load方法是只要载入到内存就会调一次,这个分类文件肯定是要装入内存的,所以头文件要不要都无所谓,没什么价值。但是你的导入#import <UIKit/UIKit.h>。因为我得拿控制器的名字
  • 我在Main.storyboard中弄多一些控制器会发现:
  • 这样的话,任何一个控制器挂了我都能监控得到

  • 这里有一个小问题:
    • 我在viewController.m文件中加上dealloc
- (void)dealloc
{
    NSLog(@"-------dealloc");
}
  • 然后让上图中的第二个控制器class为我们的viewController,运行打印,会发现
  • 按理来说,一运行点击button后点击返回是应该调用dealloc方法,但是一调用它是应该来到我们的CY_dealloc方法,应该是只打印出CY_dealloc的,但是打印出来还是调用了dealloc方法。这是为什么呢?
    • 你会发现,我们开始写Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));的时候,开始如果写的是@selector(@"dealloc"),这是禁止的,说明这个方法ARC情况下是比较特殊的。但是前面@selector(run)和@selector(study)它们交换方法后,只打印了对方的方法,自己的方法就不会调用了。
    • 但是这里这么弄也正是我要说的效果:用我们的方法交换系统的方法后还是想办到系统的方法还是能调。因为我们有时候是想在控制器死掉的时候做一些事情。比如说:在控制器死掉的时候清�掉一些东西,或者在控制器死掉的时候做一些请求:取消通知
- (void)dealloc
{
    NSLog(@"-------dealloc");

        self.data = nil;
        self.images = nil;

        [self cancel];
}
  • 但是如果按照开始的做法,我把dealloc换成CY_dealloc,就意味着我以后调dealloc只会来到CY_dealloc,以前系统的dealloc就没法执行。但是这里上面系统的dealloc可以执行,但我告诉你如果换成了普通的方法它是不可能执行的。就行前面我执行run的时候它不可能执行study,这两个方法只会执行它对应的方法
  • 所以今后你如果做了方法的调换,不是系统的,我说的是普通的方法换了。你如果调换后还想调回以前的方法,那就做一件事情执行以下:(self.对应的方法)。只有这样它才会又调回自己以前的方法。因为你这样仅仅是做一个拦截嘛!就像下面:
- (void)CY_dealloc
{
    NSLog(@"%@ - CY_dealloc", self);

        [self CY_dealloc];
        //  这样调用的是dealloc,又调回去了
}
  • 这里的意思:当一个控制器死掉的时候,它肯定会调dealloc,但dealloc方法实现被我换掉了。所以它来到了CY_dealloc。执行完- (void)CY_dealloc
    {
    NSLog(@"%@ - CY_dealloc", self);这段代码,我再调用[self CY_dealloc];,它的方法实现是dealloc,所以又调回去了。所以今后你把系统方法换掉以后,我建议你再调回去,当然有些特殊情况就特殊处理。
  • 那这个有什么价值呢?
    • 第一个以及体现了价值:拦截系统方法调用,我就可以在系统方法调用之前或者调用之后做一些事情
    • 第二个价值举个例子:
      • 使用字典和数组的时候一直有一个头疼的问题:字典和数组赋值的时候不能为空,否则报错: object cannot be nil'
    NSString *value = nil;

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    //    dict[@"name"] = value;

    NSMutableArray *array = [NSMutableArray array];
    [array addObject:value];
    array[0] = value;
  • 但是我们很多时候不知道别人给你传的值是不是空啊!为了不报错,我们经常都得进行判断:如果value有值,我再执行。
  • 那有没有一个办法可以过滤掉这些东西呢?只要发现传进来的值是个空的,我就不处理,那可不可以一劳永逸做到这点呢?我们只要写下某一段代码,以后凡是项目中传空的值进来,它都不报错了,怎么去做呢?
    • 只要将NSMutable--的addObject:方法换掉,拦截这个方法,鸭蛋发现传进来的值是空的我就不调,如果传进来的值不是nil,我就调回它以前的方法。那么这样的话今后数组的访问或者字典的访问就不会存在传进来值为空报错的问题了
  • 还举个例子:[UIImage imageName:我完全可以将imageName:方法拦截一下,我就可以对它传进来的图片名进行统一处理,比如说什么夜间模式,什么皮肤,我就可以根据文件名去加载另外一个文件,统一拦截所有图片

  • 但是还是不能乱换乱用,只是有些特殊功能可以做一下,乱用的话就不好了
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,165评论 1 23
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,156评论 9 467
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,498评论 25 707
  • 首马——从期待到终点 第一次参加马拉松比赛是2015.10.25日的合肥国际马拉松,为比赛一次次的努力奔跑,首次参...
    和妈阅读 691评论 3 0