在上一篇文章中我們理解了load&&initialize,Category---為什么只能加方法不能加屬性這篇里面我講到了分類不能添加屬性的原因,今天我們來看在分類里面如何去添加屬性
-
全局變量方法

在TCPerson+TCtest1.h中
#import "TCPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface TCPerson (TCtest1)
@property (nonatomic, strong) NSString *name;
@end
NS_ASSUME_NONNULL_END
.m
#import "TCPerson+TCtest1.h"
@implementation TCPerson (TCtest1)
NSString *name_;
- (void)setName:(NSString *)name{
name_ = name;
}
- (NSString *)name{
return name_;
}
@end
在.main中訪問
int main(int argc, const char * argv[]) {
@autoreleasepool {
TCPerson *person = [[TCPerson alloc] init];
person.name = @"jack";
NSLog(@"person-----name----%@",person.name);
}
return 0;
}
輸出結果:
2020-12-08 16:11:53.616007+0800 TCCateogry[1641:91590] person-----name----jack
Program ended with exit code: 0
缺點:當有多個對象時,name這個屬性就不知道是哪個的了,舉個例子,在main函數(shù)中:
int main(int argc, const char * argv[]) {
@autoreleasepool {
TCPerson *person = [[TCPerson alloc] init];
person.name = @"jack";
TCPerson *person1 = [[TCPerson alloc] init];
person1.name = @"rose";
NSLog(@"person-----name----%@",person.name);
NSLog(@"person1----name----%@",person1.name);
}
return 0;
}
輸出結果變成了:
2020-12-08 16:17:56.286103+0800 TCCateogry[1724:95832] person-----name----rose
2020-12-08 16:17:56.286437+0800 TCCateogry[1724:95832] person1----name----rose
-
字典方法
.m文件
#import "TCPerson+TCtest1.h"
@implementation TCPerson (TCtest1)
NSMutableDictionary *names_;
+ (void)load{
names_ = [NSMutableDictionary dictionary];
}
- (void)setName:(NSString *)name{
NSString *key = [NSString stringWithFormat:@"%p", self];
names_[key] = name;
}
- (NSString *)name{
NSString *key = [NSString stringWithFormat:@"%p", self];
return names_[key];
}
@end
在main函數(shù)里面訪問
int main(int argc, const char * argv[]) {
@autoreleasepool {
TCPerson *person = [[TCPerson alloc] init];
person.name = @"jack";
TCPerson *person1 = [[TCPerson alloc] init];
person1.name = @"rose";
NSLog(@"person-----name----%@",person.name);
NSLog(@"person1----name----%@",person1.name);
}
return 0;
}
NSString *key = [NSString stringWithFormat:@"%p", self];是將對象的地址作為key,保證key的唯一性,比如傳進來的是person,這個時候就把person的地址作為字典的key
輸出結果:2020-12-08 16:23:13.590956+0800 TCCateogry[1781:99301] person-----name----jack
2020-12-08 16:23:13.591300+0800 TCCateogry[1781:99301] person1----name----rose
優(yōu)點:
解決了上面全局變量的訪問錯亂的問題
缺點:
(1)存在內存泄漏,當你創(chuàng)建的對象不是在整個app運行的時候都存在,全局變量就浪費了內存
(2)多線程訪問的時候容易出現(xiàn)線程安全問題
(3)當需要在分類里面創(chuàng)建多個屬性的時候,需要重復寫set、get,并創(chuàng)建多個字典,比較麻煩
-
objc-runtime-objc_setAssociatedObject方法
.m文件
#import "TCPerson+TCtest1.h"
#import <objc/runtime.h>
@implementation TCPerson (TCtest1)
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name{
return objc_getAssociatedObject(self, @selector(name));
}
@end
方法說明:objc_setAssociatedObject(<#id _Nonnull object#>, <#const void * _Nonnull key#>, <#id _Nullable value#>, <#objc_AssociationPolicy policy#>)
參數(shù)1:需要添加屬性的關聯(lián)對象,在分類里面一般是self;
參數(shù)2:<#const void * _Nonnull key#>關聯(lián)的key,這里的key你可以理解成字典里面的key,這里的可以你可以寫成static const char TCNameKey,也可以寫成static const void TCNameKey = &TCNameKey;也可以寫成宏定義,盡量保證唯一并一一對應的關系,只是調用的時候objc_setAssociatedObject(self, TCNameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);注意一下,記住這個key傳的是地址
參數(shù)3:關聯(lián)的值
參數(shù)4:關聯(lián)策略
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /< Specifies a weak reference to the associated object. /
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /< Specifies a strong reference to the associated object.
* The association is not made atomically. /
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /< Specifies that the associated object is copied.
* The association is not made atomically. /
OBJC_ASSOCIATION_RETAIN = 01401, /< Specifies a strong reference to the associated object.
* The association is made atomically. /
OBJC_ASSOCIATION_COPY = 01403 /*< Specifies that the associated object is copied.
* The association is made atomically. */
};
相信一看就知道這個就明白了什么是關聯(lián)策略,它相當于我們寫屬性的時候的copy,retain等,值得注意的是,它里面沒有weak
輸出結果:
020-12-08 16:44:06.726777+0800 TCCateogry[1896:108439] person-----name----jack
2020-12-08 16:44:06.727089+0800 TCCateogry[1896:108439] person1----name----rose
在其它文章里面,有些可能這么寫get方法
#import "TCPerson+TCtest1.h"
#import <objc/runtime.h>
@implementation TCPerson (TCtest1)
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name{
return objc_getAssociatedObject(self, _cmd);
}
@end
這里的_cmd等價于@selector(name),我們在調用函數(shù)的時候,有兩個隱式參數(shù):(NSString *)name:(id self) _cmd:(SEL)_cmd,但是只能在get方法里面用_cmd,它代表的是當前方法的@selector,如果在set里面用_cmd,設置的值得key和取值的key就不一樣了
-
objc-runtime關聯(lián)對象的底層源碼解析
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
static AssociationsHashMap *_map;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
{
struct DisguisedPointerEqual {
bool operator()(uintptr_t p1, uintptr_t p2) const {
return p1 == p2;
}
};
struct DisguisedPointerHash {
uintptr_t operator()(uintptr_t k) const {
// borrowed from CFSet.c
#if __LP64__
uintptr_t a = 0x4368726973746F70ULL;
uintptr_t b = 0x686572204B616E65ULL;
#else
uintptr_t a = 0x4B616E65UL;
uintptr_t b = 0x4B616E65UL;
#endif
uintptr_t c = 1;
a += k;
#if __LP64__
a -= b; a -= c; a ^= (c >> 43);
b -= c; b -= a; b ^= (a << 9);
c -= a; c -= b; c ^= (b >> 8);
a -= b; a -= c; a ^= (c >> 38);
b -= c; b -= a; b ^= (a << 23);
c -= a; c -= b; c ^= (b >> 5);
a -= b; a -= c; a ^= (c >> 35);
b -= c; b -= a; b ^= (a << 49);
c -= a; c -= b; c ^= (b >> 11);
a -= b; a -= c; a ^= (c >> 12);
b -= c; b -= a; b ^= (a << 18);
c -= a; c -= b; c ^= (b >> 22);
#else
a -= b; a -= c; a ^= (c >> 13);
b -= c; b -= a; b ^= (a << 8);
c -= a; c -= b; c ^= (b >> 13);
a -= b; a -= c; a ^= (c >> 12);
b -= c; b -= a; b ^= (a << 16);
c -= a; c -= b; c ^= (b >> 5);
a -= b; a -= c; a ^= (c >> 3);
b -= c; b -= a; b ^= (a << 10);
c -= a; c -= b; c ^= (b >> 15);
#endif
return c;
}
};
struct ObjectPointerLess {
bool operator()(const void *p1, const void *p2) const {
return p1 < p2;
}
};
struct ObjcPointerHash {
uintptr_t operator()(void *p) const {
return DisguisedPointerHash()(uintptr_t(p));
}
};
// STL allocator that uses the runtime's internal allocator.
template <typename T> struct ObjcAllocator {
typedef T value_type;
typedef value_type* pointer;
typedef const value_type *const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
template <typename U> ObjcAllocator(const ObjcAllocator<U>&) {}
ObjcAllocator() {}
ObjcAllocator(const ObjcAllocator&) {}
~ObjcAllocator() {}
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const {
return x;
}
pointer allocate(size_type n, const_pointer = 0) {
return static_cast<pointer>(::malloc(n * sizeof(T)));
}
void deallocate(pointer p, size_type) { ::free(p); }
size_type max_size() const {
return static_cast<size_type>(-1) / sizeof(T);
}
void construct(pointer p, const value_type& x) {
new(p) value_type(x);
}
void destroy(pointer p) { p->~value_type(); }
void operator=(const ObjcAllocator&);
};
template<> struct ObjcAllocator<void> {
typedef void value_type;
typedef void* pointer;
typedef const void *const_pointer;
template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
};
typedef uintptr_t disguised_ptr_t;
inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
#if TARGET_OS_WIN32
typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
typedef hash_map<disguised_ptr_t, ObjectAssociationMap *> AssociationsHashMap;
#else
typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
#endif
}
其流程圖如下