利用Objective-C的动态运行时分配机制,可以为现有的类(自己的或系统的或三方库的)添加新方法,这种为现有的类添加新方法的方式称为类别category,他可以为任何类添加新的方法,包括那些没有源代码的类。
一. 扩展类的方法
这是我们用的最多的,现在就以扩展UIColor的一个读取16进制颜色的方法为例:
在.m文件中编写扩展的方法
+ (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha
{
//删除字符串中的空格
NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
// String should be 6 or 8 characters
if ([cString length] < 6)
{
return [UIColor clearColor];
}
// strip 0X if it appears
//如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾
if ([cString hasPrefix:@"0X"])
{
cString = [cString substringFromIndex:2];
}
//如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾
if ([cString hasPrefix:@"#"])
{
cString = [cString substringFromIndex:1];
}
if ([cString length] != 6)
{
return [UIColor clearColor];
}
// Separate into r, g, b substrings
NSRange range;
range.location = 0;
range.length = 2;
//r
NSString *rString = [cString substringWithRange:range];
//g
range.location = 2;
NSString *gString = [cString substringWithRange:range];
//b
range.location = 4;
NSString *bString = [cString substringWithRange:range];
// Scan values
unsigned int r, g, b;
[[NSScanner scannerWithString:rString] scanHexInt:&r];
[[NSScanner scannerWithString:gString] scanHexInt:&g];
[[NSScanner scannerWithString:bString] scanHexInt:&b];
return [UIColor colorWithRed:((float)r / 255.0f) green:((float)g / 255.0f) blue:((float)b / 255.0f) alpha:alpha];
}
然后在.h文件中暴露出来
/* 从十六进制字符串获取颜色 */
+ (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha;
至此,扩展系统类UIColor的方法成功,需要时,导入头文件直接使用即可:(大多数情况都直接写在PCH文件里面)
self.view.backgroundColor=[UIColor colorWithHexString:@"f7f7f9"];
二. 扩展类的属性(结合runtime可以添加属性,利用关联对象实现get和set方法)
举个例子,比如我们需要做一个button防止在规定的时间内不能连续点击的要求,我们需要向所有button添加一个属性,这个属性就是控制时间范围
.h文件里定义并暴露属性
@property (nonatomic, assign) NSTimeInterval qi_eventInterval;//button设置的时间属性
.m文件先导入<objc/runtime.h>,然后处理set和get方法
#import "UIButton+QiEventInterval.h"
#import <objc/runtime.h>static char * const qi_eventIntervalKey = "qi_eventIntervalKey";static char * const eventUnavailableKey = "eventUnavailableKey";@interface UIButton ()@property (nonatomic, assign) BOOL eventUnavailable;@end@implementation UIButton (QiEventInterval)+(void)load{ [super load]; Method method = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:)); Method qi_method = class_getInstanceMethod(self, @selector(qi_sendAction:to:forEvent:)); method_exchangeImplementations(method, qi_method);}- (void)qi_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { if (self.eventUnavailable == NO) { self.eventUnavailable = YES; [self qi_sendAction:action to:target forEvent:event]; [self performSelector:@selector(setEventUnavailable:) withObject:@(NO) afterDelay:self.qi_eventInterval]; }}-(NSTimeInterval)qi_eventInterval { return [objc_getAssociatedObject(self, qi_eventIntervalKey) doubleValue];}- (void)setQi_eventInterval:(NSTimeInterval)qi_eventInterval { objc_setAssociatedObject(self, qi_eventIntervalKey, @(qi_eventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (BOOL)eventUnavailable { return [objc_getAssociatedObject(self, eventUnavailableKey) boolValue];}- (void)setEventUnavailable:(BOOL)eventUnavailable { objc_setAssociatedObject(self, eventUnavailableKey, @(eventUnavailable), OBJC_ASSOCIATION_RETAIN_NONATOMIC);}@end
load和initialize的对比
维度
load
initialize
是否需要调用Super
不需要
不需要
调用时机
类或者category加载到runtime时由系统自动调用 在main函数之前
在类或者子类在第一次收到消息时调用(类消息或者实例消息)在main函数之后
调用方式
直接通过函数的内存地址调用的,不走msg_send,不走OC消息转发,每个类的load方法都是独立的
走OC消息转发, msg_send
调用次数
只会调用一次
1. 可能会调用多次,如果子类没有实现该方法,则子类在第一次收到消息时会调用父类的方法。2.如果在子类收到消息前,父类没有收到过消息,那么会先调用父类的initialize方法之后再调用子类的initialize方法。
调用顺序
父类-子类-category 。1.所有的类都加载完之后才会调用所有的category。 2.不同的类的load执行顺序跟编译顺序有关,可以在Target -> Build Phases -> Compile Sources 中调整顺序。但所有的category都是在所有的类的load都执行完之后,才会调的。 3.多个类的多个category或者一个类的不同category的load方法的顺序同样跟编译顺序有关,可以在compiles source中调整。
父类-子类(如果category有实现,会只调用category中的实现;父类的category会覆盖父类的实现,子类的category会覆盖子类的category实现)
使用
一般用来实现 Method Swizzle
一般用来初始化全局变量 或者 静态变量
Extension
1、 什么是extension
extension被开发者称之为扩展、延展、匿名分类。extension看起来很像一个匿名的category,但是extension和category几乎完全是两个东西。和category不同的是extension不但可以声明方法,还可以声明属性、成员变量。extension一般用于声明私有方法,私有属性,私有成员变量。
2、 extension的存在形式
category是拥有.h文件和.m文件的东西。但是extension不然。extension只存在于一个.h文件中,或者extension只能寄生于一个类的.m文件中。比如,viewController.m文件中通常寄生这么个东西,其实这就是一个extension