1.说下category原理,以及category为什么只能添加方法不能添加属性?
- 分类的实现是将category中的方法、属性、协议数据放在category_t结构体中,将结构体中的方法列表拷贝到对象的方法列表中。
- category可以添加属性,但是并不会自动生成成员变量及set、get方法,因为category_t结构体不存在成员变量列表。成员变量是在是存放在实例对象里的,并在编译阶段就已经决定好了,而分类是在运行时才去加载的。我们无法在程序运行时将分类的成员变量添加都实例对象的结构体中,因此分类中不能添加成员变量。
- category_t结构体构成
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(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
2.load 和 initialize的区别
(1)调用时机:
-
load
方法在pre-main阶段当装载类信息的时候就调用 -
initialize
是当类第一次接收到消息的时候。
(2)调用顺序:
-
load
方法的调用顺序为父类load
> 子类load
> 分类load
,且都会调用; -
initialize
调用顺序为父类initialize
> 子类或者分类initialize
,且父类initialize
只会调用一次,如果有分类initialize
会覆盖子类的initialize
(3)调用方式
-
load
方法是通过isa指针找到对应的方法与实现 -
initialize
方法是通过runtime的消息转发机制调用的
(4)实际使用
-
load
方法一般用于方法交换 -
initialize
方法一般用于初始化全局变量或静态变量。
3.分类和扩展之间的区别
(1)Category
- 不可以添加属性,需要通过runtime添加属性。
- 如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为 分类 > 本类 > 父类。因此在开发中尽量不要覆盖原有类
- 如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法
(2)Extension
- Extension是Category的一个特例。类扩展与分类相比只少了分类的名称,所以称之为“匿名分类”。一般写在.m文件中,用来扩展私有属性和方法。
- 定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有的。类扩展是在 .m 文件中声明私有方法的非常好的方式。
(3)区别
- Category只可以添加方法,Extension可以添加属性和方法。
- Extension只能用于自身类,而不能用于子类或者其他地方。
- Extension中声明的方法必须要实现,否则编译器警告。Category不会。这是因为Extension是编译阶段被添加到类中,Category是运行时被添加到类中。
4.多个分类中都有相同的方法名,执行那个分类?
-
和编译顺序有关,执行最后一个编译的分类
-
可以在
imagebuildPhases->Compile Sources
里面调整编译顺序
5.如何给category添加属性
(1)方式一
1在.h中
@property(nonatomic,copy)NSString *name;
2在.m中
static NSString *_name;
-(void)setName:(NSString *)name
{
_name = name;
}
-(NSString *)name
{
return _name;
}
缺点:_name静态全局变量与类并没有关联,无论对象创建与销毁,只要程序在运行_name变量就存在,并不是真正意义上的属性。
(1)方式二
1在.h中
@property(nonatomic,copy)NSString *age;
2在.m中
-(void)setAge:(NSString *)age{
//参数一:给那个对象添加属性
//参数二:属性名
//参数三:关联的值,也就是set方法存入值给属性去保存
//策略
objc_setAssociatedObject(self, @"age",age, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)age
{
return objc_getAssociatedObject(self, @"age");
}
6.给分类关联对象实现原理
- 关联对象并不是放在了原来的对象里面,而是自己维护了一个全局的map用来存放每一个对象及其对应关联属性表格
- 关联对象并不是存储在被关联对象本身内存中,而是存储在全局的统一的一个AssociationsManager中,如果设置关联对象为nil,就相当于是移除关联对象
作者:喜剧收尾_XWX
链接:https://www.jianshu.com/p/d3b9d6184793
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友评论