赞
踩
代理,又称委托代理(delegate),是iOS中常用的一种设计模式。顾名思义,它是把某个对象要做的事委托给别的对象去做。那么别的对象就是这个对象的代理,代替它来打理要做的事,这个对象就是别的对象的委托。
协议,是多个类共享的一个方法列表,在协议中所列出的方法没有响应的实现,由其他类来实现
委托,指给一个对象提供机会对另一个对象中的变化做出反应或者响应另一个对象的行为,其基本思想就是协同解决问题。
在iOS程序设计中,委托通过一种@protocol
的方式来实现,所以又称为协议。UITableView, UITextField等都用到这种设计模式。
委托的工作:
代理的工作
具体实现代码参考博客:
.h
#import "ECOPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface ECOPerson (Work)
- (void)performDaysWork;
- (void)takeVacationFromWork;
@end
NS_ASSUME_NONNULL_END
.m
#import "ECOPerson+Work.h"
@implementation ECOPerson (Work)
- (void)performDaysWork {
}
- (void)takeVacationFromWork {
}
@end
文件名:主类名+分类名
调用方法时,只需要向主类引用发送消息即可
先看一段代码
@interface Person (Friendship)
@property (nonatomic, strong) NSString *friends;
- (void)addFriend:(Person*)person;
- (void)removeFriend:(Person*)person;
- (BOOL)isFriendWith:(Person*)person;
@end
编译会报错
意思是说此分类无法合成与friends属性相关的实例变量.
我们先看一看分类的本质
分类(Category
)是OC中的特有语法,它是表示一个指向分类的结构体的指针
。原则上它只能增加方法,不能增加成员(实例)变量。具体原因看源码组成:
在分类的结构体指针中,没有属性列表,只有方法列表。所以原则上它只能添加方法,不能添加属性(成员变量),实例上可以通过其他方式添加属性
。
问题:
为什么在分类中声明属性时,运行不会出错?既然分类不让添加属性,为什么写@property仍然可以编译通过?
我们知道用@property声明属性时,编译器会自动帮我们生成_成员变量和setter和getter方法,但在分类的结构体指针中根本没有成员列表,所以在分类中用@property声明属性,即无法生成_成员变量和setter和getter方法,只有getter和setter和声明。编译和运行都可以通过。如果调用了_成员变量和setter/getter方法,报错就会发生。
OC是动态的,我们可以手动添加。
一种常见的方法就是通过runtime.h 中objc_getAssociatedObject / objc_setAssociatedObject来访问和生成关联对象。通过这种方法来模拟生成对象。
在其他地方使用
注意,以上代码只是手动实现了setter/getter方法,但调用_成员变量依然会报错。
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
//相当于 setValue:forKey 进行关联value对象
//objc_AssociationPolicy 属性 是设定该value在object内的属性,即 assgin, (retain,nonatomic)...等
id objc_getAssociatedObject(id object, const void *key);
//用来读取对象
void objc_removeAssociatedObjects(id object);
//函数来移除一个关联对象,或者使用objc_setAssociatedObject函数将key指定的关联对象设置为nil。
OBJC_ASSOCIATION_ASSIGN 等价于 @property(assign)。
OBJC_ASSOCIATION_RETAIN_NONATOMIC等价于 @property(strong, nonatomic)。
OBJC_ASSOCIATION_COPY_NONATOMIC等价于@property(copy, nonatomic)。
OBJC_ASSOCIATION_RETAIN等价于@property(strong,atomic)。
OBJC_ASSOCIATION_COPY等价于@property(copy, atomic)。
我们在代码中使用了 OBJC_ASSOCIATION_RETAIN_NONATOMIC 就相当于使用了 nonatomic 和 strong 修饰符。
源码实现
void
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
{
_object_set_associative_reference(object, key, value, policy);
}
再看_object_set_associative_reference方法
void _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy) { // 这段代码在以前当 object 和 key 为 nil 时是有效的。某些代码可能依赖于此以避免崩溃。显式检查和处理。 // rdar://problem/44094390 if (!object && !value) return; // 如果 object 和 value 均为 nil,直接返回,避免崩溃 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}; // 创建 DisguisedPtr 包装的对象指针和 ObjcAssociation 对象,用于关联对象的存储 // 在锁之外对新值进行 retain 操作 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; } // 如果 value 不为空,则将对象和一个新的关联对象映射插入哈希映射中,并标记为第一个关联对象 /* 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); } } } // 如果 value 为空,则从哈希映射中删除指定键对应的关联对象 } } // 在锁之外调用 setHasAssociatedObjects 方法,如果对象实现了 _noteAssociatedObjects 方法,则会触发调用,并且可能触发 +initialize 方法,从而执行其他操作,包括设置更多的关联对象 if (isFirstAssociation) object->setHasAssociatedObjects(); // 在锁之外释放旧值 association.releaseHeldValue(); }
运行思路可以总结如下:
nil
,如果都是 nil
,则直接返回,避免崩溃。DisguisedPtr
对象,用于包装对象指针,以及一个 ObjcAssociation
对象,用于存储关联对象的策略和值。retain
操作,确保新值在函数执行期间有效。setHasAssociatedObjects
方法,如果对象实现了 _noteAssociatedObjects
方法,则会触发调用,并且可能触发 +initialize 方法,从而执行其他操作,包括设置更多的关联对象。id _object_get_associative_reference(id object, const void *key) { ObjcAssociation association{}; // 创建一个空的 ObjcAssociation 对象,用于存储关联对象的策略和值 { AssociationsManager manager; // 创建 AssociationsManager 对象,用于管理关联对象的哈希表 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 = j->second; // 将查找到的关联对象赋值给 association association.retainReturnedValue(); // 对返回的关联对象进行保留操作,以便在返回后保持有效 } } } return association.autoreleaseReturnedValue(); // 对返回的关联对象进行自动释放操作,并返回该关联对象 }
这段代码的运行思路如下:
首先,创建一个空的 ObjcAssociation
对象 association
,用于存储关联对象的策略和值。
创建 AssociationsManager
对象 manager
,用于管理关联对象的哈希表。
通过 manager.get()
获取关联对象的哈希表 associations
。
在 associations
哈希表中查找指定对象 (objc_object *)object
的关联对象集合,使用 associations.find()
方法进行查找,返回一个迭代器 i
。
如果找到了指定对象的关联对象集合,进入内部的 if
代码块。
在关联对象集合 refs
中查找指定键 key
的关联对象,使用 refs.find()
方法进行查找,返回一个迭代器 j
。
如果找到了指定键的关联对象,进入内部的第二个 if
代码块。
将找到的关联对象赋值给 association
。
对返回的关联对象进行保留操作,使用 association.retainReturnedValue()
方法。
返回自动释放后的关联对象,使用 association.autoreleaseReturnedValue()
方法。
总体而言,这段代码的目的是在给定的对象中查找指定键的关联对象,并返回关联对象的值。它通过迭代访问嵌套的哈希表结构来实现这一目标,并使用 ObjcAssociation
类来封装和管理关联对象的策略和值。
void _object_remove_associations(id object, bool deallocating) { ObjectAssociationMap refs{}; // 创建一个空的 ObjectAssociationMap 对象,用于存储关联对象的集合 { AssociationsManager manager; // 创建 AssociationsManager 对象,用于管理关联对象的哈希表 AssociationsHashMap &associations(manager.get()); // 获取关联对象的哈希表 AssociationsHashMap::iterator i = associations.find((objc_object *)object); // 在哈希表中查找指定对象的关联对象集合 if (i != associations.end()) { refs.swap(i->second); // 交换指定对象的关联对象集合和空的 refs,将关联对象集合存储在 refs 中 // 如果不是正在释放对象,则保留 SYSTEM_OBJECT 类型的关联对象 bool didReInsert = false; if (!deallocating) { for (auto &ref : refs) { if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) { i->second.insert(ref); // 将 SYSTEM_OBJECT 类型的关联对象重新插入到关联对象集合中 didReInsert = true; } } } if (!didReInsert) associations.erase(i); // 如果没有重新插入 SYSTEM_OBJECT 类型的关联对象,则从哈希表中删除关联对象集合 } } SmallVector<ObjcAssociation *, 4> laterRefs; // 创建一个 SmallVector 对象,用于存储稍后释放的关联对象 // 释放所有关联对象(在锁的外部进行) for (auto &i : refs) { if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) { // 如果不是正在释放对象,则将 RELEASE_LATER 类型的关联对象存储在 laterRefs 中 if (deallocating) laterRefs.append(&i.second); } else { i.second.releaseHeldValue(); // 释放普通类型的关联对象的值 } } for (auto *later : laterRefs) { later->releaseHeldValue(); // 释放稍后释放的关联对象的值 } }
这段代码的运行思路如下:
首先创建一个空的 ObjectAssociationMap
对象 refs
,用于存储关联对象的集合。
创建 AssociationsManager
对象 manager
,用于管理关联对象的哈希表。
通过 manager.get()
获取关联对象的哈希表 associations
。
在 associations
哈希表中查找指定对象 (objc_object *)object
的关联对象集合,使用 associations.find()
方法进行查找,返回一个迭代器 i
。
如果找到了指定对象的关联对象集合,进入内部的 if
代码块。
将指定对象的关联对象集合和空的 refs
进行交换,将关联对象集合存储在 refs
中。
如果不是正在释放对象(deallocating
为假),则遍历 refs
中的关联对象。
对于具有 OBJC_ASSOCIATION_SYSTEM_OBJECT
策略的关联对象,重新插入到关联对象集合中,以保留这些关联对象。
如果没有重新插入 OBJC_ASSOCIATION_SYSTEM_OBJECT
类型的关联对象,则从哈希表中删除关联对象集合。
创建一个 SmallVector
对象 laterRefs
,用于存储稍后需要释放的关联对象。
遍历 refs
中的关联对象。
对于具有 OBJC_ASSOCIATION_SYSTEM_OBJECT
策略的关联对象,如果正在释放对象(deallocating
为真),将其存储在 laterRefs
中,稍后释放。
对于普通类型的关联对象,调用 releaseHeldValue()
方法释放其值。
遍历 laterRefs
中的稍后释放的关联对象。
对于 laterRefs
中的关联对象,调用 releaseHeldValue()
方法释放其值。
这段代码的功能是移除指定对象的关联对象,并根据标志 deallocating
决定是否保留特定类型的关联对象。它通过使用哈希表和向量
来存储和管理关联对象,并根据不同的条件执行相应的操作,最终释放关联对象的值。
下面具体看一看实现关联对象技术的四个核心对象
class AssociationsManager { using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>; // 使用 DisguisedPtr<objc_object> 作为键,ObjectAssociationMap 作为值的哈希映射类型别名 static Storage _mapStorage; // 静态成员变量,用于存储对象的关联对象的哈希映射 public: AssociationsManager() { AssociationsManagerLock.lock(); } // 构造函数,在创建 AssociationsManager 实例时加锁 ~AssociationsManager() { AssociationsManagerLock.unlock(); } // 析构函数,在销毁 AssociationsManager 实例时解锁 AssociationsHashMap& get() { return _mapStorage.get(); } // 返回对象的关联对象的哈希映射 static void init() { _mapStorage.init(); } // 静态方法,用于初始化关联对象的哈希映射 };
AssociationsManager
类具体实现了以下功能:
_mapStorage
存储对象的关联对象的哈希映射。AssociationsManager
实例时对关联对象的访问是线程安全的。AssociationsManager
提供了 get()
方法,用于获取对象的关联对象的哈希映射。这样,其他代码可以通过调用 get()
方法来获取特定对象的关联对象,并进行操作。AssociationsManager
还提供了 init()
方法,用于初始化关联对象的哈希映射。这个方法在适当的时候被调用,以确保在使用关联对象之前,相关的数据结构已经被正确初始化。class ObjcAssociation { uintptr_t _policy; id _value; public: ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {} // 构造函数,使用给定的策略和值初始化 ObjcAssociation 对象 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; } // 对返回的关联值进行自动释放操作,如果策略需要自动释放内存 };
ObjcAssociation
类的实例对象可以用于存储一个对象的关联对象的策略和值。它的成员变量 _policy 存储关联对象的策略信息,_value 存储关联对象的值。
该类提供了以下主要功能和方法:
ObjcAssociation
对象,并指定关联对象的策略和值。policy()
方法可以获取关联对象的策略,通过 value() 方法可以获取关联对象的值。acquireValue()
方法用于根据策略进行内存管理,它会根据策略对关联对象的值进行保留操作或复制操作。releaseHeldValue()
方法用于释放持有的关联值,如果策略需要释放内存。retainReturnedValue()
方法用于对返回的关联值进行保留操作,如果策略需要保留内存。autoreleaseReturnedValue()
方法用于对返回的关联值进行自动释放操作,如果策略需要自动释放内存。typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
这段代码定义了两个类型别名:ObjectAssociationMap
和 AssociationsHashMap
,它们都是基于 LLVM 的 DenseMap 实现的哈希表。
ObjectAssociationMap
是一个哈希表,用于存储指向 ObjcAssociation
类对象的指针和对应的关联对象。它的键类型是 const void *
,即指向常量空指针的指针类型,通常用作关联对象的标识符。值类型是 ObjcAssociation
,即 ObjcAssociation
类的对象,用于存储关联对象的策略和值。AssociationsHashMap
是一个嵌套的哈希表,用于存储 Objective-C
对象和其关联对象的映射关系。它的键类型是 DisguisedPtr<objc_object>
,是一个包装了 objc_object
类型的指针的类模板,用于隐藏指针的真实类型。值类型是 ObjectAssociationMap
,即前面提到的 ObjectAssociationMap
类型,用于存储指向 ObjcAssociation
类对象的指针和对应的关联对象。Objective-C
对象的关联对象。ObjectAssociationMap
通过将关联对象的标识符作为键,与实际的 ObjcAssociation 对象建立映射关系。而 AssociationsHashMap
则通过将 Objective-C 对象的指针作为键,与 ObjectAssociationMap 建立映射关系,从而实现了快速查找某个对象的关联对象的能力。
总结一下这几个核心类的关系
拓展是分类的一种特殊形式
@interface 主类类名()
@end
拓展通常定义在主类的.m文件中,拓展声明的方法直接在主类的.m文件中实现
方式一:以单独的文件定义
XYZPopViewController_ExViewController.h文件
#import"XYZPopViewController.h"
@interface XYZPopViewController()
@property(nonatomic,strong)NSString*stringOfEx;
- (void)testEx;
@end
在主类的.m文件中定义
XYZPopViewController.m文件
#import"XYZPopViewController.h"
@interface XYZPopViewController()
@property(nonatomic,strong)NSString*stringOfEx;
- (void)testEx;
@end
在主类的.m文件中实现拓展定义的方法
@implementation XYZPopViewController
- (void)testEx {
self.stringOfEx=@"给扩展里面定义的属性字符串赋值";
NSLog(@"定义的属性String是:%@",self.stringOfEx);
}
@end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。