IOS 用Runtime给分类添加属性、方法

分类(category):可以在不修改原来类的基础上,为一个类扩展方法。该方法可以不写实现(.m)。category是在程序加载运行的时候,才加载到内存中,此时类已经创建完成,内存已经申请了,因此不能再更改。但是每个对象结构体都存储着相对应的hashmap来表达类的名字、方法、属性等等。本文介绍一下利用runtime给该哈希表添加属性、方法,以及多个分类的加载和调用顺序。

扩展 (extension):category的一个特例,有时候也被称为匿名分类。他的作用是为一个类添加一些私有的成员变量和方法。多用于.m 

最简单最常见的扩展,.m中文件头部的扩展:

@interface ViewController ()

@end


创建分类:

IOS 用Runtime给分类添加属性、方法_第1张图片
IOS 用Runtime给分类添加属性、方法_第2张图片
IOS 用Runtime给分类添加属性、方法_第3张图片


我这里是创建了两个分类,一个用于测试公有,一个用于测试私有.

PS:有时候会根据不同业务去创建不同的分类。或者类过大也会创建多个分类。

说下多个分类的问题:

1.分类与分类之间的方法和属性不能互相调用,但是都可以调用基类。

2.分类中原则上只能添加私有属性,下面会讲到通过runtime添加公有属性。而且setter和getter方法需要手动实现。

3.分类中方法名字重复的话会优先调用私有的。在本类和分类有相同的方法时,直接覆盖基类方法。

如果有两个分类,他们都实现了相同的方法,如何判断谁先执行?分类执行顺序可以通过targets,Build Phases,Complie Source进行调节,注意执行顺序是从上到下的。(只有两个相同方法名的分类)

category 方法可能会覆盖于同一个类class 的其它 category 中的方法。但也可能被覆盖,因为不法预知他们的加载优先顺序,出现这种情况通常会在编译时出错。如果在一个开发的SDK中使用了类别, 就最好保证类别名不同于使用者的类别名,以及类别方法也不同于使用者的类别方法名, 通常通过加前缀来做到。

注意:这里经过测试,分类的调用跟创建(添加进项目)时间有关,跟调用顺序无关。如果多个分类方法名冲突,会调用最后创建的那一个,前面会被覆盖。(更正一下,创建不是指创建时间,而是指加入项目的时间,即便你去年就创建了分类A,现在创建了分类B。然后先加入B,后加入A。调用的方法还是优先调用A)

另外一点,试验证明,你创建了分类,即便头文件不引入,只要方法名相同,还是会调用后来创建的。因为这代表调用了私有方法。

(这个结论是亲自测试的,网上搜半天并没有得到什么有用的答案。还是自己亲自来靠谱)

4.分类的方法优先级高,但是不可以调用super方法。

利用category添加属性。

这里分别是两种不同类型的属性,基本类型和对象类型。

IOS 用Runtime给分类添加属性、方法_第4张图片

/*

* id object 给哪个对象的属性赋值

const void *key 属性对应的key

id value  设置属性值为value

objc_AssociationPolicy policy  使用的策略,是一个枚举值,和copy,retain,assign是一样的,IOS开发一般都选择NONATOMIC

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);

*/

源对象,关联时的用来标记是哪一个属性的key(因为你可能要添加很多属性),关联的对象和一个关联策略。

用来标记是哪一个属性的key常见有三种写法:

static NSNumber *numKey;

static void *numKey = &numKey;

static char numKey;


更新:

分类的命名要尽量带前缀,方便区分。

runtime  严谨写法:

- (NSString*)YYURLString{

return  objc_getAssociatedObject(self, ((__bridge void*)PageNavigationURLStringKey));

}

- (void)setYYURLString:(NSString*)urlString{

objc_setAssociatedObject(self, ((__bridge void*)PageNavigationURLStringKey),urlString, OBJC_ASSOCIATION_RETAIN);

}

主要在于前面加了个__bridge void *    实现id类型与void*类型的相互转换。方便通用OC和C指针,这点在MRC转ARC的时候用的比较多。ARC下的void和id不互通造成的。


拓展:这种利用Runtime赋值的方式,可以将某个对象赋值给某个类,相当于页面间传值。

你可能感兴趣的:(IOS 用Runtime给分类添加属性、方法)