首先可以查看下官方对于Category的介绍:Apple官方文档解释

个人理解:

  1. Category初衷是用来给现有类添加或者重写函数的,不可添加属性,这个区别于Extension;
  2. 将类的一些类似的功能拆分出去,使得每个分类中的函数功能统一,便于管理;
  3. 给系统类(NSString,NSArray等)添加一些自定义的方法;
  4. 重写私有类的函数(公有、私有均可),改变内部的实现代码。

另类使用:

  1. 因为Category是不具有属性列表的,所以不能给Category添加属性。那怎么通过@property给定义的Catogory添加属性呢?接下来就详细讲解:

定义一个NSObject的分类Worker,通过@property声明两个属性如下:

@interface NSObject (Worker)
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *jodName;
@end

如果这时直接使用name和jodName如下:

NSObject *worker = [[NSObject alloc] init];
worker.name = @"Jim";
worker.jodName = @"Software Engineer";

command+R运行则会报如下错误:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject setName:]: unrecognized selector sent to instance 0x600001a7c570'

这是因为系统不会给Category添加属性的setter/getter方法,所以这时就要用到runtime机制了,写法如下:

// name的key
static NSString *keyName;
// jobName的key
static NSString *keyJobName;@implementation NSObject (Worker)
- (void)setName:(NSString *)name {objc_setAssociatedObject(self, &keyName, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name {return objc_getAssociatedObject(self, &keyName);
}
- (void)setJodName:(NSString *)jodName {objc_setAssociatedObject(self, &keyJobName, jodName, OBJC_ASSOCIATION_COPY);
}
- (NSString *)jodName {return objc_getAssociatedObject(self, &keyJobName);
}
@end

setter 里面使用了一个 objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy) 方法,这个方法有四个参数,分别是:

  1. 源对象(self)
  2. 关联时的用来标记是哪一个属性的key (keyName,keyJobName)
  3. 关联的对象(name、jobName)
  4. 一个关联策略(OBJC_ASSOCIATION_COPY)

getter 里面用到了 objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key) 这个方法,这个方法有两个参数,填写方法参照setter方法。

这时再运行代码如下:

NSObject *worker = [[NSObject alloc] init];
worker.name = @"Jim";
worker.jodName = @"Software Engineer";
NSLog(@"Hello, my name is %@, I am a %@ !",worker.name,worker.jodName);

打印信息如下:

2019-04-30 11:24:43.258099+0800 test[44759:2009573] Hello, my name is Jim, I am a Software Engineer !

这样就实现了在变相的在分类里添加了假属性,注意:这种添加方式并不支持_name和_jobName的方式调用方式,因为真正意思上并没有相关属性

  1. 重写类的方法,并介绍如何调用原类方法:

在分类中重写了原类中的方法,在调用该方法时,程序是触发的分类中重写的方法,但有时我们还是需要用到原类中的方法,接下来就看下怎么用代码实现:

先生成一个Person类,实现一个方法:

- (NSString *)getWorkerIntroducation {return @"该方法由分类实现的,返回的是这个人的介绍信息";
}

在生成一个分类Person+Worker.h,重写方法如下:

- (NSString *)getWorkerIntroducation {if (self.name.length&&self.jodName.length) {return [NSString stringWithFormat:@"My name is %@, I am a %@!", self.name, self.jodName];} else {return nil;}
}

在调用如下:

Person *worker = [[Person alloc] init];
worker.name = @"Jim";
worker.jodName = @"Software Engineer";
NSLog(@"%@", [worker getWorkerIntroducation]);

打印日志:

2019-04-30 15:59:05.812732+0800 test[46479:2079803] My name is Jim, I am a Software Engineer!

根据分类的原理,结构体中存在一个对象方法列表,主要就是通过获取方法的列表实现调用原类的方法,我们可以先打印一下对象方法列表:

Person *worker = [[Person alloc] init];
worker.name = @"Jim";
worker.jodName = @"Software Engineer";
NSLog(@"%@", [worker getWorkerIntroducation]);// 打印对象方法列表
u_int count;
Method *methods = class_copyMethodList([worker class], &count);
for (int i=0; i<count; i++) {SEL sel = method_getName(methods[i]);NSString *methodName = [NSString stringWithCString:sel_getName(sel)     encoding:NSUTF8StringEncoding];NSLog(@"%d = %@", i, methodName);
}
// 打印结果
// 调用分类重写方法
My name is Jim, I am a Software Engineer!
0 = setJodName:
// 分类的重写方法
1 = getWorkerIntroducation
// 原类的重写方法
2 = getWorkerIntroducation
3 = jodName
4 = name
5 = setName:

从打印信息来看分类的调用优先级是在前面的,所以需要遍历获取最后的方法再通过IMP调用,代码如下:

Person *worker = [[Person alloc] init];
worker.name = @"Jim";
worker.jodName = @"Software Engineer";
NSLog(@"%@", [worker getWorkerIntroducation]);// 打印对象方法列表
u_int count;
Method *methods = class_copyMethodList([worker class], &count);
int index = -1;
for (int i=0; i<count; i++) {SEL sel = method_getName(methods[i]);NSString *methodName = [NSString stringWithCString:sel_getName(sel) encoding:NSUTF8StringEncoding];if ([methodName isEqualToString:@"getWorkerIntroducation"]) {index = i;}
}
if (index>=0) {// 通过index获取原类的方法SEL sel = method_getName(methods[index]);IMP imp = method_getImplementation(methods[index]);NSString *string = ((id (*)(id, SEL)) imp)(self, sel);NSLog(@"%@",string);
} else {NSLog(@"不存在该方法");
}

打印结果:

// 执行的分类方法
2019-04-30 17:21:50.688067+0800 test[49470:2130743] My name is Jim, I am a Software Engineer!
// 执行的原类方法
2019-04-30 17:21:50.688238+0800 test[49470:2130743] 该方法由分类实现的,返回的是这个人的介绍信息

代码传送门:

  • 样例代码

传送门:

  • IMP原理介绍
  • Category底层实现原理小记

分类(category)的使用相关推荐

  1. iOS之深入解析分类Category的底层原理

    一.Category 简介 Objective-C 中的 Category 是对装饰模式的一种具体实现.它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法. 分类 Category 可以 ...

  2. Objective-C分类 (category)和扩展(Extension)

    1.分类(category) 使用Object-C中的分类,是一种编译时的手段,允许我们通过给一个类添加方法来扩充它(但是通过category不能添加新的实例变量),并且我们不需要访问类中的代码就可以 ...

  3. oc的分类category

    category分类,指的是可以将类无尽的拓展起方法,是不能拓展属性的.类名后面有个括号代表这个类是分类eg: #import "Student.h" @interface Stu ...

  4. WordPress插件:WP No Category Base 去除分类Category目录

    不少折腾WordPress的朋友都希望去掉分类链接中的 /category/ 目录标志,网上很多这方面的教程,据倡萌所知,除了使用 WP No Category Base 插件(或类似插件),其他的方 ...

  5. category显示html,wordpress文章.html伪静态,分类category和页面设置 - 搜外SEO问答

    最近把网站文章整顿了一番,将所有文章都伪静态了一番.文章统一增加html后缀,并且分类去掉了category,页面增加了html. 1,所有所有分类目录去掉category,安装WP No Categ ...

  6. ios 分类(Category)

      今天研究了类别,都是网上找的资料,类别的作用 类别主要有3个作用:        (1)将类的实现分散到多个不同文件或多个不同框架中.        (2)创建对私有方法的前向引用.        ...

  7. OC特有语法:分类category,给NSString增加方法计算字符串中数字的个数

    1:分类的使用场景:想对一个类,扩充一些功能,而又不改变原来类的模型,也不用继承,这时OC中的特有语法:分类可以做到: 当然分类也是一个类,也需要声明和实现,声明在.h文件中,实现在.m文件中,格式如 ...

  8. woocommerce 分类到菜单_Woocommerce教程:添加和编辑产品Category分类

    在使用WordPress的woocommerce商城插件制定商城的时候, 如果你公司拥有众多产品, 那么应该需要给这些产品添加Category分类, 以便分类管理产品, 让网站访客更快地找到自己想要的 ...

  9. oc 协议 回调 静态成员_OC底层原理探究:Category、关联对象和block本质

    1.分类Category的使用 // 给MJPerson类添加分类 @interface MJPerson : NSObject - (void)run; @end@implementation MJ ...

  10. (iOS-基本知识)Category VS Extension 原理详解

    1.什么是Category? category是Objective-C 2.0之后添加的语言特性,别人口中的分类.类别其实都是指的category.category的主要作用是为已经存在的类添加方法. ...

最新文章

  1. 学了python能干啥-python都可以做什么用
  2. Linux常用命令——paste
  3. 【TDS学习文档4】IBM Directory schema的管理2——object class
  4. 如何使用Angular的@Input()装饰器
  5. 关灯看视频(Turn Off the Lights)
  6. [蓝桥杯2019初赛]矩形切割-找规律
  7. QQ音乐:React v16 新特性实践
  8. 泉州中考分数如何计算机,2019年泉州中考总分多少分,泉州中考考试科目设置
  9. QItemSelectionModel——视图选择
  10. Pycharm在创建py文件时, 如何自动添加文件头注释?
  11. PyQt5教程 - QtDesigner窗口设计工具的使用
  12. 桌面计算机恢复出厂设置,windows7电脑怎么恢复出厂设置
  13. Hbuilder连接苹果手机
  14. 分享老齐【学方法】宽信用周期对股市的影响!
  15. 5G路测下行速率优化指导书
  16. python--快速启动Server
  17. 基于ZigBee的出租车调度系统
  18. 移动端图片变模糊问题
  19. 企业级监控系统zabbix---通过qqmail发送告警信息
  20. Comet OJ - Contest #10 沉鱼落雁

热门文章

  1. EXCEL身份证号码校验以及提取基本信息
  2. 国内外已知SAR卫星相关信息整理(持续更新-2022.9.6)
  3. ESP8266作为无线串口设置
  4. 侯世达:为什么人工智能还远远谈不上「智能」?
  5. ORACLE公司传奇历史
  6. vi和nano实用操作
  7. java web play_玩转 Java Web 应用开发:Play 框架
  8. Installing Kubernetes Using ‘kubeadm’
  9. rundll32.exe传入参数
  10. 【FLASH实例1000教程】(4)上