extension(类扩展)和 category(类别)
extension(类扩展)
简单来说,extension在.m文件中添加,所以其权限为private,所以只能拿到源码的类添加extension。另外extension是编译时决议,和interface和implement里的代码融合在一块了一般。
category(类别) category能在不继承类的情况下给类动态添加方法。
、创建category
关于@dynamic的特性及用法可参考:
https://blog.csdn.net/qq_28446287/article/details/79094491
、category的优缺点 可以将类的实现代码分散到多个不同的文件或框架中
创建对私有方法的前向引用
OC语法中,你不能对一个类的方法定义为private,只有+、-,对实例变量可以private、public。
具体可参考此文档http://www.cnblogs.com/stevenwuzheng/p/5457487.html 向对象添加非正式协议
对NSObject进行一个类别叫做非正式协议,可以只实现想要的方法 、category在runtime中的源码 typedef struct objc_category *Category; struct objc_category
{
//类别的名字
char * _Nonnull category_name OBJC2_UNAVAILABLE;
//该类的名字
char * _Nonnull class_name OBJC2_UNAVAILABLE;
//实例方法列表
struct objc_method_list * _Nullable instance_methods OBJC2_UNAVAILABLE;
//类方法列表
struct objc_method_list * _Nullable class_methods OBJC2_UNAVAILABLE;
//协议列表
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE; 明显看出,Category没有容纳变量的地方。
、category的原理 objective-c的运行依赖runtime,runtime依赖于dyld动态加载,查看objc-os.mm文件发现其调用栈如下: void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true; // fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init(); // Register for unmap first, in case some +load unmaps something
_dyld_register_func_for_remove_image(&unmap_image);
dyld_register_image_state_change_handler(dyld_image_state_bound,
/*batch*/, &map_2_images);
dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, /*not batch*/, &load_images);
} category被附加到类上是在map_images的时候发生的。在new-ABI的标准下,_objc_init函数里调用的map_iamges最终会调用objc-runtime-new.mm中的_read_images函数。_read_images中部分代码: // Discover categories.
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
for (i = ; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls); if (!cls) {
// Category's target class is missing (probably weak-linked).
// Disavow any knowledge of this category.
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
} // Process this category.
// First, register the category with its target class.
// Then, rebuild the class's method lists (etc) if
// the class is realized.
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
} if (cat->classMethods || cat->protocols
/* || cat->classProperties */)
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
} ts.log("IMAGE TIMES: discover categories"); 从代码可以看出: 将category和它的主类(或元类)注册到哈希表中.
如果主类(或元类)已经实现,那么重建它的方列表。 category中的实例方法和属性被整合到主类中,而类方法被整合到元类中。而协议被同时整合到了主类和元类中。 /***********************************************************************
* remethodizeClass
* Attach outstanding categories to an existing class.
* Fixes up cls's method list, protocol list, and property list.
* Updates method caches for cls and its subclasses.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta; runtimeLock.assertWriting(); isMeta = cls->isMetaClass(); // Re-methodizing: check for more categories
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
} attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
} 如注释所说,此函数将未处理的category整合到主类(或元类),整合cls的方法、协议、属性列表,更新cls及其子类的方法缓存。 查看其中的attachCategories函数: // Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats); bool isMeta = cls->isMetaClass(); // fixme rearrange to remove these intermediate allocations
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists)); // Count backwards through cats to get newest categories first
int mcount = ;
int propcount = ;
int protocount = ;
int i = cats->count;
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[i]; method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
} property_list_t *proplist = entry.cat->propertiesForMeta(isMeta);
if (proplist) {
proplists[propcount++] = proplist;
} protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
} auto rw = cls->data(); prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > ) flushCaches(cls); rw->properties.attachLists(proplists, propcount);
free(proplists); rw->protocols.attachLists(protolists, protocount);
free(protolists);
} 通过while循环,遍历所有的category,得每个category的方法列表mlist、proplist和protolist并存入主类(或元类)的mlists、proplists和protolists中。从而更新类的数据字段data()中mlist、proplist和protolist的值。 category没有替换掉原来类的方法,也就是说如果category和原来类有method1,那么在将category整合到原来类之后,类的method_list会有两个method1 category中的method1会被放在类的method_list前面,而原来类的method1被放 到了method_list后面,在调用时候会先调用method_list前面的,所以看起来是将原来类的method1覆盖了,实际上并不是那么回事。
、category的两个面试题 3.1 一个类和它的category同时拥有一个方法,在调用时候调用哪一个?答案参考“、category的原理” 3.2 一个类有多个category并都拥有同一个方法,在调用时候调用哪一个?答案参考“、category的原理” 举个例子: //#import "type.m"
- (void)test
{
NSLog(@"type class!!");
} //#import "type+xxxx.m"
- (void)test
{
NSLog(@"category xxxx");
} //#import "type+xxxx1.m"
- (void)test
{
NSLog(@"category xxxx1");
} //#import "type+xxxx2.m"
- (void)test
{
NSLog(@"category xxxx2");
}
可以知道,输出的结果跟compile source文件中的category的.m文件放置顺序有关。且放最底部的时候输出(主类.m文件的放置不影响,理由参考”、category的原理”)
、category动态添加变量 @interface type (xxxx) @property (nonatomic, assign) NSInteger number; @end static void *numberKey = &numberKey; @implementation type (xxxx) - (void)setNumber:(NSInteger)number
{
objc_setAssociatedObject(self, &numberKey, [NSString stringWithFormat:@"%ld",number], OBJC_ASSOCIATION_ASSIGN); } - (NSInteger)number
{
return [objc_getAssociatedObject(self, &numberKey) integerValue];
} @end objc_setAssociatedObject和objc_getAssociatedObject的描述可参考:https://www.cnblogs.com/liuting-1204/p/6526342.html
extension(类扩展)和 category(类别)的更多相关文章
- 类扩展和category的小区别
类扩展可以给类声明新的变量(属性),但是方法的实现只能在.m中实现 category可以给类声明新的方法实例,但是不可以添加变量(属性)
- iOS中的分类(category)和类扩展(extension)
今天在研究swift的时候看到了分类和扩展.这是两个十分重要有用的功能,但是之前用的不多,没有深入了解过,在今天就从头理一遍. 一.分类(Category): 概念: 分类(Category)是OC中 ...
- iOS分类(category),类扩展(extension)—史上最全攻略
背景: 在大型项目,企业级开发中多人同时维护同一个类,此时程序员A因为某项需求只想给当前类currentClass添加一个方法newMethod,那该怎么办呢? 最简单粗暴的方式是把newMethod ...
- 类目(category) - 类扩展(extension) 区别
说明: 方法,属性或变量: 类别只能添加方法,不能添加属性(理论上,但可以通过runtime的关联添加). 扩展可以添加方法和实例变量或属性,实例变量默认@private类型.扩展是类别的一个特例 ...
- OC分类(类目/类别) 和 类扩展 - 全解析
OC分类(类目/类别) 和 类扩展 - 全解析 具体见: oschina -> MyDemo -> 011.FoundationLog-OC分类剖析 http://blog.csdn. ...
- 分类(类别/Category)与 类扩展(Extension)
一.分类(类别/Category) 1.适用范围 当你已经封装好了一个类(也可能是系统类.第三方库),不想在改动这个类了,可是随着程序功能的增加需要在类中增加一个方法,这时我们不必修改主类, ...
- iOS category 类别 和 extension 扩展
category 类别 又称为 分类 在ios项目开发中允许使用类别为现有的类添加新的方法,并不需要创建子类.通过类别我们可以动态地为现有的类添加新的方法,可以将类的定义模块化地布局到多个相关文件中 ...
- [ios]objective-c中Category类别(扩展类)专题总结
本文转载至 http://yul100887.blog.163.com/blog/static/20033613520126333344127/ objective-c类别的作用?通过类别的方式, ...
- 分类(Category)的本质 及其与类扩展(Extension) /继承(Inherit)的区别
1.分类的概念 分类是为了扩展系统类的方法而产生的一种方式,其作用就是在不修改原有类的基础上,为一个类扩展方法,最主要的是可以给系统类扩展我们自己定义的方法. 如何创建一个分类?↓↓ ()Cmd+N, ...
随机推荐
- python-30个骚操作
1.冒泡排序 2.计算x的n次方的方法 3.计算a*a + b*b + c*c + …… 4.计算阶乘 n! 5.列出当前目录下的所有文件和目录名 ...
- C++ list 查找
#include <iostream>#include <list>#include <algorithm> using namespace std; int ma ...
- postgresql之 drop & delete & truncate
官网:https://www.postgresql.org/docs/8.1/sql-droptable.html Name DROP TABLE -- remove a table Synopsis ...
- 阶段5 3.微服务项目【学成在线】_day02 CMS前端开发_05-vuejs研究-vuejs基础-v-text指令
把js移到body 的下面 网速改慢一点 通过模拟网速慢的情况.刷新页面的时候会有闪烁的效果 速度快的情况下也会闪烁 ,只不过是不明显. 2.解决插值表达式闪烁问题,使用v-text v-text可以 ...
- Java 检查IPv6地址的合法性
Java 检查IPv6地址的合法性 由于IPv4资源即将耗尽,IPv6将要正式启用,这是大势所趋. 一些现有的服务和应用逐步要对IPv6支持,目前还处在过渡阶段. 提前了解一些IPv6的知识,还是有必 ...
- js的event详解
event代表事件的状态,例如触发event对象的元素.鼠标的位置及状态.按下的键等等.event对象只在事件发生的过程中才有效.event的某些属性只对特定的事件有意义.比如,fromElement ...
- 安装“Microsoft SQL Server 2014 Management Objects”时报错"Error Writing to file: Microsoft.SqlServer.XEvent.Linq.dll."
问题: 当安装的软件依赖Microsoft SQL Server 2014 Management Objects时,会把这个组件打进安装包里,但是在服务器上安装时却报如下错误: “Error Writ ...
- composer全量镜像使用方法
原文网址:https://pkg.phpcomposer.com/ Packagist 镜像使用方法 还没安装 Composer 吗?请往下看如何安装 Composer . 镜像用法 有两种方式启用本 ...
- (IStool)软件打包时当文件存在时不覆盖文件(配置文件)
需求:程序实际使用过程中有些配置信息是需要用户手动配置的,不同客户使用配置信息也不同,所以软件发布前需要考虑这个问题,覆盖安装时需要忽略这些配置文件 实现:当对应的目录下由此文件的时候不覆盖此文件 [ ...
- IO-file-03 文件的长度
package com.bwie.io; import java.io.File; public class FileDemo4 { /**文件字节数 * length():字节数 文件夹 0 * * ...