之前在看一些第三方源码的时候,时不时的能碰到一些关于运行时相关的代码。于是乎,就阅读了一些关于运行时的文章,感觉写的都不错,写此篇文章为了记录一下,同时也重新学习一遍。
objc_msgSend
,只有对象才能发送消息,因此以objc
开头。注意:在oc中,不论是实例对象还是Class,都是id类型的对象 总结:到这里,我们可以看到调用方法的本质就是发送消息了,并且可以看到我们写的
NSObject *obj = [[NSObject alloc] init];
上面这条语句发送了两次消息,第一次发送了alloc
消息,第二次发送了init
消息。
相信大家都知道分类是不可以添加属性的,不过我们可以通过运行时,给分类动态的添加属性。
原理:给一个分类声明属性,其本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。
NSObject
添加一个分类,然后声明一个name
属性。#import <Foundation/Foundation.h>
@interface NSObject (Extension)
@property (nonatomic, copy) NSString *name;
@end
NSObject
对象,然后给name
属性赋值,并且打印name
的值。
我们可以看到编译成功,但是运行的时候就会华丽丽的崩溃,这就是所谓的不能给分类添加属性的原因了。不过我们可以通过运行时实现。name
属性的setter
以及getter
方法。NSObject
的Student
类,声明一个study
方法。#import <Foundation/Foundation.h>
@interface Student : NSObject
-(void)study;
@end
Student
对象,调用study
方法。 Student *s = [[Student alloc] init];
[s performSelector:@selector(study)];
此时会出现一个经典的报错: -[Student study]: unrecognized selector sent to instance 0x7fd719cbb2f0 错误原因:调用一个未实现的实例方法
Student
类.m文件动态添加study方法的实现。交换方法实现
Method Swizzling
。场景一:统计整个项目每一个页面停留时长。(解决方法也不是唯一的)
可是一开始写项目的时候,并没有使用到继承,所以又papapa地就整个项目的控制器都继承于一个基类,重复地将每一个控制器的继承都该成了我们创建的基类。但是,这样解决真的好么,有可能我们有些界面是继承自UITableViewController
的,UICollectionViewController
,等等。那么你就可能会对这些控制器再单独的写上面的代码了。
好不容易将整个项目改过来了,然后某天,公司来了一位新人,你告诉他所有的类都要继承自你写的那个基类,新手总是会不经意地犯错误(也有可能是人家还没有习惯),有些类忘记继承了,后期排查起来费力费时。那么有没有更好地解决方式呢?方式三就可以处理这种问题。
Method Swizzling
实现,给UIViewController
写一个分类。这个单独开一篇给大家讲讲吧。
希望通过本文能让大家学习到一些关于Runtime的知识,如果有什么疑问,欢迎大家一起讨论。