1.动态添加方法
定义一个类叫MCPerson, 让他调用一个hello方法.
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
MCPerson *person = [[MCPerson alloc]init];
[person performSelector:@selector(hello)];
}
@end
因为OC的运行时机制,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用.
MCPerson并没有hello这个方法,但是编译的时候不会出现问题, 在运行的时候才报错.提示没有这个方法.
这里我们用运行时为MCPerson类添加一个方法
@implementation MCPerson
void hello(id self, SEL sel) {
NSLog(@"你好");
}
//当一个对象调用未实现的方法,就会调用这个方法
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(hello)) {
//第一个参数<#__unsafe_unretained Class cls#>:需要添加方法的类
//第二个参数:<#SEL name#>方法名称
//第三个参数:<#IMP imp#>方法实现
//第四个参数:<#const char *types#>函数类型v:void @:对象->self :表示SEL->_cmd
class_addMethod(self, @selector(hello), hello, "v@:");
}
return [super resolveInstanceMethod:sel];
}
@end
当再次运行的时候就会调用+ (BOOL)resolveInstanceMethod:(SEL)sel这个方法,在这个方法内部为MCPerson添加方法,于是就不报错了.
2.关联方法
关联方法可以将两个对象关联起来.
分类中的属性是不会自动生成setter和getter方法以及成员变量的.setter和getter方法可以自己手动写,成员变量就需要用到运行时的关联方法.
在UIImageView中写一个属性
@interface UIImageView (category)
//分类中的属性不会自动生成setter和getter方法以及成员变量
@property(nonatomic, copy)NSString *currentAddr;
@end
#import "UIImageView+category.h"
#import <objc/runtime.h>
@implementation UIImageView (category)
const char *webCacheKey = "webCacheKey";
1. (void)setCurrentAddr:(NSString *)currentAddr{
//将当前类和它属性关联起来
//1.要关联的对象 2.关联的key 3.要关联的值 4.关联策略
objc_setAssociatedObject(self, webCacheKey, self.currentAddr, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
2. (NSString *)currentAddr {
//1.要关联的对象 2.关联的key
return objc_getAssociatedObject(self, webCacheKey);
}
@end
用这个方法可以移除关联
objc_removeAssociatedObjects(self)
3.交换方法
交换方法可以将两个方法进行交换.
这里举例将UIImageView的initWithImage:方法换成一个自定义的方法.
#import "UIImageView+category.h"
#import <objc/runtime.h>
+ (void)initialize{
Method m1 = class_getInstanceMethod([UIImageView class], @selector(initWithImage:));
Method m2 = class_getInstanceMethod([UIImageView class], @selector(initWithResizableImage:));
method_exchangeImplementations(m1, m2);
}
- (instancetype)initWithResizableImage:(UIImage *)image {
NSLog(@"新方法");
return [[UIImageView alloc]init];
}
@end
当再次调用UIImageView的initWithImage:方法的时候就会调用自定义方法.
[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"heart.png"]];
4.遍历属性
UIPageControl是没办法直接将显示页数的圆点替换成图片的,因为_pageImage, _currentPageImage是私有属性.
我们这里就利用runtime遍历UIPageControl的所有私有属性.
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([UIPageControl class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
//获取所有属性
const char *property = ivar_getName(ivar);
NSLog(@"%@",[[NSString alloc]initWithCString:property encoding:NSUTF8StringEncoding]);
}
打印结果
最后再利用KVC给私有属性赋值就可以实现效果.
[self.pageControl setValue:[UIImage imageNamed:@"compose_keyboard_dot_normal"] forKey:@"_pageImage"];
[self.pageControl setValue:[UIImage imageNamed:@"compose_keyboard_dot_selected"] forKey:@"_currentPageImage"];