扩展
#import "HFPerson.h"
@interface HFPerson ()
@property (nonatomic, copy) NSString *myName;
- (void)testAction;
@end
@implementation HFPerson
- (void)testAction {
}
@end
扩展是编译时决议的(也就是编译时添加到主类中),以声明的形式存在,多数情况下寄生在宿主类的.m文件中,不能为看不到源码的类或系统类添加扩展。
可以添加是有属性、方法、成员变量,一般是用于声明私有的。
Category
先来看一下category的源码:
struct _category_t {
const char *name; //1
struct _class_t *cls;//2
const struct _method_list_t *instance_methods;//3
const struct _method_list_t *class_methods;//4
const struct _protocol_list_t *protocols;//5
const struct _prop_list_t *properties;//6
};
oc中所有类和对象都是c结构体,category也不例外。
1、name,注意:不是(HFCategory),而是主类名。
2、cls:要扩展的类对象,编译阶段这个值为0,runtime加载时才会根据name对应到类对象。
3、instance_methods:这个category所有的实例方法。
4、class_methods:这个category所有的类方法。
5、protocols:这个category所遵循的协议列表。
6、properties:这个category所有的property,这也是分类可以定义属性的原因,但是这个property不会自动生成实例变量。需要使用关联对象实现,和普通的实例变量是两码事。
再来看实例:
#import "HFPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface HFPerson (HFCategory)
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *cateName;
- (void)instanceTestAction;
@end
NS_ASSUME_NONNULL_END
#import "HFPerson+HFCategory.h"
#import <objc/runtime.h>
static NSString *cateNameKey = @"cateNameKey";
static NSString *nameKey = @"nameKey";
@implementation HFPerson (HFCategory)
- (void)instanceTestAction {
}
@end
此时若调用name,会出现:
需要我们手动实现
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, &nameKey);
}
终端到文件目录后 执行clang -rewrite-objc HFPerson+HFCategory.m,获得cpp文件
可得
static struct _category_t _OBJC_$_CATEGORY_HFPerson_$_HFCategory __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"HFPerson",
0, // &OBJC_CLASS_$_HFPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_HFPerson_$_HFCategory,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_HFPerson_$_HFCategory,
};
_OBJC_$_CATEGORY_HFPerson_$_HFCategory
可以发现编译后的category内的数据,因为我们添加了属性和实例方法,所以对应两行有被填充,并且这两行具体的内部实现,如下:
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[3];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_HFPerson_$_HFCategory __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
3,
{{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_HFPerson_HFCategory_setName_},
{(struct objc_selector *)"name", "@16@0:8", (void *)_I_HFPerson_HFCategory_name},
{(struct objc_selector *)"instanceTestAction", "v16@0:8", (void *)_I_HFPerson_HFCategory_instanceTestAction}}
};
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_HFPerson_$_HFCategory __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
2,
{{"name","T@\"NSString\",C,N"},
{"cateName","T@\"NSString\",C,N"}}
};
最终这个类的category们生成了一个数组,存在了__DATA段下的__objc_catlistsection里
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_HFPerson_$_HFCategory,
};
到此我们可知:
分类是运行时决议的,有单独的.h和.m文件。可以为看不到源码的类/系统类添加分类。
分类可以添加实例方法、类方法、协议、属性
(但是setter/getter方法需要使用关联对象实现且添加的属性没有对应的没有成员变量(因此分类无法添加成员变量)。
分类可以拆解体积庞大的类文件。
注意:
如果分类中有和主类同名的方法,执行顺序是分类>主类>父类。(尽量不要和主类同名)
若多个分类中有同名方法,那么编译器会执行最后一个参与编译分类中的方法。
运行时动态的将分类中的方法合并到主类中,根据消息传递的方法查找,所以后编译进去的会被先查找到。
关联对象
我们已经知道分类中可以声明属性,但是其对应的setter/getter方法,需要使用关联对象来实现。
如分类中提及到的
- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, &nameKey);
}
源码解析
id
objc_getAssociatedObject(id object, const void *key)
{
return _object_get_associative_reference(object, key);
}
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_associations(object, /*deallocating*/false);
}
}
继续查看相关底层源码
_object_set_associative_reference
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
// This code used to work when nil was passed for object and key. Some code
// probably relies on that to not crash. Check and handle it explicitly.
// rdar://problem/44094390
if (!object && !value) return;
//检查对象类是否禁止使用关联对象
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
//创建DisguisedPtr 包装对象指针
DisguisedPtr<objc_object> disguised{(objc_object *)object};
// 创建ObjcAssociation对象关联信息
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
bool isFirstAssociation = false;
{
//创建AssociationsManager对象,负责管理关联对象的映射
AssociationsManager manager;
//获取全局唯一的静态哈希map
AssociationsHashMap &associations(manager.get());
//value 有值则 将映射关系插入到关联对象的映射表中
//无值 则移除此关联对象
if (value) {
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
if (refs_result.second) {
/* it's the first association we make */
isFirstAssociation = true;
}
/* establish or replace the association */
auto &refs = refs_result.first->second;
auto result = refs.try_emplace(key, std::move(association));
if (!result.second) {
association.swap(result.first->second);
}
} else {
auto refs_it = associations.find(disguised);
if (refs_it != associations.end()) {
auto &refs = refs_it->second;
auto it = refs.find(key);
if (it != refs.end()) {
association.swap(it->second);
refs.erase(it);
if (refs.size() == 0) {
associations.erase(refs_it);
}
}
}
}
}
// Call setHasAssociatedObjects outside the lock, since this
// will call the object's _noteAssociatedObjects method if it
// has one, and this may trigger +initialize which might do
// arbitrary stuff, including setting more associated objects.
if (isFirstAssociation)
object->setHasAssociatedObjects();
// release the old value (outside of the lock).
association.releaseHeldValue();
}
设值流程为:
1、创建一个AssociationsManager管理类
2、获取唯一全局静态哈希Map
3、判断是否存在关联对象值
若存在:存入到ObjectAssociationMap中
若不存在:移除此关联对象
_object_get_associative_reference
ObjectAssociationMapid
_object_get_associative_reference(id object, const void *key)
{
//创建一个空的association对象,用来存储关联信息
ObjcAssociation association{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
// 在关联对象映射表中查找对象
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
//如果找到了对象,则在关联对象映射表中查找对应的的关联应用。
ObjectAssociationMap &refs = i->second;
ObjectAssociationMap::iterator j = refs.find(key);
if (j != refs.end()) {
//如果找到了关联应用,则将其赋值给association
association = j->second;
association.retainReturnedValue();
}
}
}
return association.autoreleaseReturnedValue();
}
取值流程:
1、创建一个AssociationsManager管理类
2、获取唯一全局静态哈希Map
3、根据对象类查找,查找到到对应的ObjectAssociationMap,
4、在ObjectAssociationMap中根据key找到对应的ObjectAssociation,最终返回对应value
_object_remove_associations
void
_object_remove_associations(id object, bool deallocating)
{
ObjectAssociationMap refs{};
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
AssociationsHashMap::iterator i = associations.find((objc_object *)object);
if (i != associations.end()) {
/找到对象后,则将其关联对象映射赋值给 refs,并从关联对象映射表中移除该对象的映射
refs.swap(i->second);
// If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
bool didReInsert = false;
if (!deallocating) {
for (auto &ref: refs) {
if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
i->second.insert(ref);
didReInsert = true;
}
}
}
if (!didReInsert)
associations.erase(i);
}
}
// Associations to be released after the normal ones.
SmallVector<ObjcAssociation *, 4> laterRefs;
// release everything (outside of the lock).
for (auto &i: refs) {
if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
// If we are not deallocating, then RELEASE_LATER associations don't get released.
if (deallocating)
laterRefs.append(&i.second);
} else {
i.second.releaseHeldValue();
}
}
for (auto *later: laterRefs) {
later->releaseHeldValue();
}
}
销毁流程:
对象dealloc是,销毁相关管理对象。
调用流程是:dealloc->_objc_rootDealloc->rootDealloc->object_dispose->objc_destructInstance->_object_remove_associations
涉及到4个重要的数据结构
1、AssociationsManager:
class AssociationsManager {
using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
static Storage _mapStorage;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {
return _mapStorage.get();
}
static void init() {
_mapStorage.init();
}
};
2、AssociationsHashMap:
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
3、ObjectAssociationMap
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
4、ObjcAssociation
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
ObjcAssociation(const ObjcAssociation &other) = default;
ObjcAssociation &operator=(const ObjcAssociation &other) = default;
ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
swap(other);
}
inline void swap(ObjcAssociation &other) {
std::swap(_policy, other._policy);
std::swap(_value, other._value);
}
inline uintptr_t policy() const { return _policy; }
inline id value() const { return _value; }
inline void acquireValue() {
if (_value) {
switch (_policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
_value = objc_retain(_value);
break;
case OBJC_ASSOCIATION_SETTER_COPY:
_value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
break;
}
}
}
inline void releaseHeldValue() {
if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
objc_release(_value);
}
}
inline void retainReturnedValue() {
if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
objc_retain(_value);
}
}
inline id autoreleaseReturnedValue() {
if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
return objc_autorelease(_value);
}
return _value;
}
};
其他
enum {
OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
OBJC_ASSOCIATION_SETTER_RETAIN = 1,
OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8),
OBJC_ASSOCIATION_SYSTEM_OBJECT = _OBJC_ASSOCIATION_SYSTEM_OBJECT, // 1 << 16
};
ExplicitInitLock<spinlock_t> AssociationsManagerLock;