分类和类扩展简介
分类:
categoty
- 专门用来给类添加新方法。
- 不能给类添加成员属性,添加了成员变量也无法取到,但是可以通过runtime给分类添加属性。
- 分类中用@property定义变量,只会生成变量的getter,setter方法的声明,不能生成方法实现和带下划线的成员变量。
类扩展:
extension
- 特殊的分类,也叫做匿名分类。
- 可以给类添加成员属性,但是是私有变量。
- 可以给类添加方法,也是私有方法。
类扩展extension
新建一个类扩展,扩展必须定义在@interface 和 @implementation 之间
@interface LKPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
- (void)instanceMethod;
+ (void)classMethod;
@end
@interface LKPerson ()
@property (nonatomic, copy) NSString *ext_name;
@property (nonatomic, assign) int ext_age;
- (void)ext_instanceMethod;
+ (void)ext_classMethod;
@end
@implementation LKPerson
- (void)instanceMethod{
NSLog(@"%s",__func__);
}
+ (void)classMethod{
NSLog(@"%s",__func__);
}
- (void)ext_instanceMethod{
NSLog(@"%s",__func__);
}
+ (void)ext_classMethod{
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
}
return 0;
}
利用clang -rewrite-objc main.m -o main.cpp转化cpp文件,
extern "C" unsigned long OBJC_IVAR_$_LKPerson$_name;
extern "C" unsigned long OBJC_IVAR_$_LKPerson$_age;
extern "C" unsigned long OBJC_IVAR_$_LKPerson$_ext_name;
extern "C" unsigned long OBJC_IVAR_$_LKPerson$_ext_age;
struct LKPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
int _ext_age;
NSString *_name;
NSString *_ext_name;
};
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[18];
} _OBJC_$_INSTANCE_METHODS_LKPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
18,
{{(struct objc_selector *)"instanceMethod", "v16@0:8", (void *)_I_LKPerson_instanceMethod},
{(struct objc_selector *)"ext_instanceMethod", "v16@0:8", (void *)_I_LKPerson_ext_instanceMethod},
{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LKPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LKPerson_setName_},
{(struct objc_selector *)"age", "i16@0:8", (void *)_I_LKPerson_age},
{(struct objc_selector *)"setAge:", "v20@0:8i16", (void *)_I_LKPerson_setAge_},
{(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_LKPerson_ext_name},
{(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_LKPerson_setExt_name_},
{(struct objc_selector *)"ext_age", "i16@0:8", (void *)_I_LKPerson_ext_age},
{(struct objc_selector *)"setExt_age:", "v20@0:8i16", (void *)_I_LKPerson_setExt_age_},
{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LKPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LKPerson_setName_},
{(struct objc_selector *)"age", "i16@0:8", (void *)_I_LKPerson_age},
{(struct objc_selector *)"setAge:", "v20@0:8i16", (void *)_I_LKPerson_setAge_},
{(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_LKPerson_ext_name},
{(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_LKPerson_setExt_name_},
{(struct objc_selector *)"ext_age", "i16@0:8", (void *)_I_LKPerson_ext_age},
{(struct objc_selector *)"setExt_age:", "v20@0:8i16", (void *)_I_LKPerson_setExt_age_}}
};
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CLASS_METHODS_LKPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"classMethod", "v16@0:8", (void *)_C_LKPerson_classMethod},
{(struct objc_selector *)"ext_classMethod", "v16@0:8", (void *)_C_LKPerson_ext_classMethod}}
};
发现属性和方法都已经存在了。
添加扩展之后加载流程
在realizeClassWithoutSwift添加断点,打印ro,
发现方法都已经有了,而扩展的属性
set和get方法没有,原因是并未引入#import "LKTeacher+LK.h"。
关联对象
不能给类添加成员属性,添加了成员变量也无法取到,但是可以通过runtime给分类添加属性。
@property (nonatomic, copy) NSString *categoty_name;
- (void)setCategoty_name:(NSString *)categoty_name{
objc_setAssociatedObject(self, "categoty_name", categoty_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)categoty_name{
objc_getAssociatedObject(self, "categoty_name");
}
查看objc_setAssociatedObject,这种设计模式属于是接口模式,这种模式下对外的接口不变,也就是外部调用永远是调用objc_setAssociatedObject,内部的逻辑变化不影响外部的调用。
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
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<objc_object> disguised{(objc_object *)object};
ObjcAssociation association{policy, value};
// retain the new value (if any) outside the lock.
association.acquireValue();
bool isFirstAssociation = false;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.get());
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();
}
DisguisedPtr<objc_object> disguised{(objc_object *)object};,将object包装成统一的数据格式DisguisedPtr。
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();
}
};
AssociationsManager析构函数,开始调用执行AssociationsManager() { AssociationsManagerLock.lock(); }加锁,作用域结束调用~AssociationsManager() { AssociationsManagerLock.unlock(); }解锁。
static Storage _mapStorage;静态变量,所以AssociationsHashMap &associations(manager.get());是单例模式,只有一张AssociationsHashMap表。
template <typename... Ts>
std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
BucketT *TheBucket;
if (LookupBucketFor(Key, TheBucket))
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
false); // Already in map.
// Otherwise, insert the new element.
TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
return std::make_pair(
makeIterator(TheBucket, getBucketsEnd(), true),
true);
}
(objc::AssociationsHashMap &) associations = 0x000000010037f088: {
Buckets = nil
NumEntries = 0
NumTombstones = 0
NumBuckets = 0
}
可以看到associations里面为空
auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});创建一个空的ObjectAssociationMap,去取查询的键值对,如果发现没有这个 key 就插入一个 空的 BucketT进去并返回true。
auto result = refs.try_emplace(key, std::move(association));将association里面的policy 和 value存进去。
以下内容引用自KC课件
关联对象: 设值流程
- 1: 创建一个 AssociationsManager 管理类
- 2: 获取唯一的全局静态哈希Map
- 3: 判断是否插入的关联值是否存在:
- 3.1: 存在走第4步
- 3.2: 不存在就走 : 关联对象插入空流程
- 4: 创建一个空的 ObjectAssociationMap 去取查询的键值对
- 5: 如果发现没有这个 key 就插入一个 空的 BucketT进去 返回
- 6: 标记对象存在关联对象
- 7: 用当前 修饰策略 和 值 组成了一个 ObjcAssociation 替换原来 BucketT 中的空
- 8: 标记一下 ObjectAssociationMap 的第一次为 false
关联对象插入空流程
- 1: 根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
- 2: 清理迭代器
- 3: 其实如果插入空置 相当于清除
关联对象: 取值流程
- 1: 创建一个 AssociationsManager 管理类
- 2: 获取唯一的全局静态哈希Map
- 3: 根据 DisguisedPtr 找到 AssociationsHashMap 中的 iterator 迭代查询器
- 4: 如果这个迭代查询器不是最后一个 获取 : ObjectAssociationMap (这里有策略和value)
- 5: 找到ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value
- 6: 返回_value