在OC 中,使用category会让我们在开发中非常方便,可以为某个类增添方法,对类别自己有一点小小的体会,首先先来介绍一下类别
类别是在运行时决定的,在运运行时才会分配相应的内存空间,在就决定了在编译期是不能给某个类增加属性,否则会影响内存布局,从而导致crash ,若想给某个类增加一个属性,需要在运行时添加,首先先介绍下类别的结构
typedef struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
} category_t;
这个结构体理念包含这个类别的名字,实例方法,类方法,协议名,和实例属性
正如我们所知的那样,当我们重写类的一个方法以后,会覆盖原来类的方法,它为什么会覆盖原来类的方法呢,我们先来看一下当调用一个方法的时候是怎么运行的
BOOL classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (isRealized(cls)) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
getName(cls), cat->name,
classExists ? "on existing class" : "");
}
}
if (cat->classMethods || cat->protocols
/* || cat->classProperties */)
{
addUnattachedCategoryForClass(cat, cls->isa, hi);//关联映射
if (isRealized(cls->isa)) {
remethodizeClass(cls->isa);//添加事件
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
getName(cls), cat->name);
}
}
在这里面我们可以看到,是先根据isa 指针找到该类,然后在该类的方法列表里面找到该方法的实现,当重写一个类的方法后,它会将重写的方法,放在方法列表的第一位,当第一次找到该方法的时候就不会在查找,就会执行重写的方法实现,如果我们想执行原来的方法实现就可以跳过方法列表前面的方法实现,执行最后一个该方法的实现就可以了
Class currentClass = [MyClass class];
MyClass *my = [[MyClass alloc] init];
if (currentClass) {
unsigned int methodCount;
Method *methodList = class_copyMethodList(currentClass, &methodCount);
IMP lastImp = NULL;
SEL lastSel = NULL;
for (NSInteger i = 0; i < methodCount; i++) {
Method method = methodList[i];
NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(method))
encoding:NSUTF8StringEncoding];
if ([@"printName" isEqualToString:methodName]) {
lastImp = method_getImplementation(method);
lastSel = method_getName(method);
}
}
typedef void (*fn)(id,SEL);
if (lastImp != NULL) {
fn f = (fn)lastImp;
f(my,lastSel);
}
free(methodList);
}
上面的printName为重写的方法名,获取最后一个类名和方法实现,这样就可以调用被重写的方法实现。
前面提到不可以在编译期给类增加属性,那可以在运行期动态的给某个类添加属性,可以调用message API来实现
+ (void)load
{
NSLog(@"%@",@"load in Category1");
}
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self,
"name",
name,
OBJC_ASSOCIATION_COPY);
}
- (NSString*)name
{
NSString *nameObject = objc_getAssociatedObject(self, "name");
return nameObject;
}
我们对某个类得分类中增加一个name 属性,重写name 的set 和get方法就可以为其添加属性了,此外给大家奉上我做的一个相关的demo 地址类别的方法覆盖和添加属性此外在此谢谢美团技术团队的技术分享美团类别分享