1.动态添加方法
定义一个类叫MCPerson, 让他调用一个hello方法.
@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; MCPerson *person = [[MCPerson alloc]init]; [person performSelector:@selector(hello)]; }@end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
因为OC的运行时机制,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用.
MCPerson并没有hello这个方法,但是编译的时候不会出现问题, 在运行的时候才报错.提示没有这个方法.
这里我们用运行时为MCPerson类添加一个方法
@implementation MCPersonvoid 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
当再次运行的时候就会调用+ (BOOL)resolveInstanceMethod:(SEL)sel这个方法,在这个方法内部为MCPerson添加方法,于是就不报错了.
2.关联方法
关联方法可以将两个对象关联起来.
分类中的属性是不会自动生成setter和getter方法以及成员变量的.setter和getter方法可以自己手动写,成员变量就需要用到运行时的关联方法.
在UIImageView中写一个属性
@interface UIImageView (category)//分类中的属性不会自动生成setter和getter方法以及成员变量@property(nonatomic, copy)NSString *currentAddr;@end
- 1
- 2
- 3
- 4
#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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
用这个方法可以移除关联
objc_removeAssociatedObjects(self)
- 1
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
当再次调用UIImageView的initWithImage:方法的时候就会调用自定义方法.
[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"heart.png"]];
- 1
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]); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
打印结果
最后再利用KVC给私有属性赋值就可以实现效果.
[self.pageControl setValue:[UIImage imageNamed:@"compose_keyboard_dot_normal"] forKey:@"_pageImage"]; [self.pageControl setValue:[UIImage imageNamed:@"compose_keyboard_dot_selected"] forKey:@"_currentPageImage"];
- 1
- 2