iOS的分类(Category)

turboksiOS229

一:Category的底层结构

定义在objc-runtime-new.h中:

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;

    // Fields below this point are not always present on disk.

    struct property_list_t *_classProperties;

    method_list_t*methodsForMeta(boolisMeta) {

        if(isMeta)returnclassMethods;

        else return instanceMethods;

    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);

};

二:Category的加载处理的过程

1.通过Runtime加载某个类的所有Category数据

2.把所有Category的方法、属性、协议数据,合并到一个大数组中

    ----后边参与编译的Category数据,会在数组的前边

3.将合并的分类数据(方法、协议、属性),插入到类原来数据的前面


接下来证明一下方法是否真的可以添加成功:

创建一个NSObject的分类

NSObject+KS.h:

@interface NSObject (KS)

-(void)Turbo;

-(void)KS;

@end

NSObject+KS.m:

@implementation NSObject (KS)

- (void)Turbo{

}

- (void)KS{

}

@end

ViewController.m中:

#import "ViewController.h"

#import "NSObject+KS.h"

#import

@interface ViewController ()

@property (nonatomic, strong) NSObject * obj;

@end

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    [self getMethod];

}

-(void)getMethod{

    unsignedintcount;

    Method *methods = class_copyMethodList([NSObject class], &count);

    for(inti =0; i < count; i++)

    {

        Methodmethod = methods[i];

        SELselector =method_getName(method);

        NSString*name =NSStringFromSelector(selector);

        NSLog(@"%@",name);

    }

}

@end


打印结果如下:



001.png

打印结果

可以看出、我们自己添加的方法确实合并到了NSObject的类中,并且还处于头部的位置,由此可以证明上边的理论是正确的!

三:+load方法

1.    +load方法会在runtime加载类、分类的时候调用

2.    每个类、分类的+load方法,在程序运行过程中只调用一次

3.    调用顺序:

    -    先调用类的+load

        --    按照边币先后顺序调用(先编译,先调用)

        --    调用子类的+load之前会先调用父类的+load

    -    再调用分类的+load

        --    按照编译的先后顺序调用(先编译,先调用)

+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用

四:+initialize方法

    1.    +initialize方法会在类第一次接收到消息的时候调用

    2.    调用顺序

        -    先调用父类的+initialize,再调用子类的+initialize

        -    先初始化父类,再初始化子类,每个类只会初始化1次

五:+initialize和+load的区别?

+initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以会有以下特点:

1.    如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)

2.    如果分类实现了+initialize,就会覆盖类本身的+initialize调用

六:如何给分类添加成员变量?

默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但是可以通过关联对象来间接实现。

1.    添加关联对象

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,  id_Nullablevalue,objc_AssociationPolicypolicy)

2.    获取关联对象

objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

3.    移除所有的关联对象

objc_removeAssociatedObjects(id _Nonnull object)

补充:

key的常见方法:

static void *MyKey = &MyKey;

objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)

objc_getAssociatedObject(obj, MyKey)

static char MyKey;

objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)

objc_getAssociatedObject(obj, &MyKey)

使用属性名作为key

objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

objc_getAssociatedObject(obj, @"property");

使用get方法的@selecor作为key

objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)

objc_getAssociatedObject(obj, @selector(getter))


002.png

objc_AssociationPolicypolicy


关联对象的原理:



代码实现给分类添加属性:

NSObject+KS.h

@interface NSObject (KS)

@property (nonatomic, strong) NSString * ksname;

@end

NSObject+KS.m 

- (NSString *)ksname{

    return objc_getAssociatedObject(self, @"ksname");

}

- (void)setKsname:(NSString*)ksname{

    objc_setAssociatedObject(self, @"ksname", ksname, OBJC_ASSOCIATION_COPY_NONATOMIC);

}

ViewController.m 中使用

- (void)viewDidLoad {

    [super viewDidLoad];

    NSObject * obj = [[NSObject alloc] init];

    obj.ksname=@"呵呵";

    NSLog(@"%@",obj.ksname);

}

打印结果



003.png

打印结果



七:关联对象的原理



004.png

原理



005.png

原理



返回列表

上一篇:Runtime详解

下一篇:iOS多线程

相关文章

百度地图自定义marker

百度地图自定义marker

#import "HomeViewController.h"#import "PaopaoView.h"@interface HomeViewControlle...

iOS多线程

iOS多线程

多线程常见方案一、GCD的函数:GCD中有2个用来执行任务的函数queue:队列block:任务1.    用同步的方式执行任务dispatch_sync(di...

Runtime详解

Runtime详解

The Objective-C language defers as many decisions as it can from compile time and link time to runti...

多线程举例

多线程举例

举例一:- (void)viewDidLoad {    [super viewDidLoad];    dispatch_queue_t queue = di...

iOS 仿抖音打卡美好中国

iOS 仿抖音打卡美好中国

效果图这几天打卡中国挺火的,刚好有点时间就想着看看能不能模仿一下。Scrollview + CADisplayLink 设置背景UILongPressGestureRecognizer长按手...

iOS 之导航渐变,并且跳转返回保持滑动状态

iOS 之导航渐变,并且跳转返回保持滑动状态

导航刚开始进入为透明,根据我们的滑动确定要显示的背景色。先看一下效果图:一:创建demo,将3个界面添加到控制器中,此过程比较简单代码就不上了。二:在第一页的viewDidload中将当前页面的导航设...