一直都知道分類里面添加屬性,會提示黃色警告,使用的時候運行到這里會crash,那么就真的不能給分類里面添加屬性嗎?答案當然是可以的,那么怎么添加呢,那么我們先創(chuàng)建一個Person的分類,繼承NSObject
,在.h文件里面添加一條name的屬性
@property (nonatomic, copy) NSString *name;
當然也可以在.m匿名分類里面添加屬性,只是這樣的屬性只能在這個分類里面使用,不能在類的實例中使用。.h文件中添加的在類的實例中也可以使用。然后在.m引入runtime的頭文件
#import <objc/runtime.h> 或者 #import <objc/message.h> //這兩種都可以
接下來先在.m文件為我們的name屬性添加一個key
static NSString *nameKey = @"nameKey"; //name的key
這時候執(zhí)行Xcode命令command + b,在匿名分類里面就會出現(xiàn)黃色警報,如下
Property 'name' requires method 'name' to be defined - use @dynamic or provide a method implementation in this category
運行會crash并提示
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject setName:]: unrecognized selector sent to instance 0x600000002db0'
這句崩潰的提示是找不到setName:
方法,下面就是給分類屬性添加setter方法
- (void)setName:(NSString *)name {~~
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
在setName:
里面使用了一個objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
方法,這個方法有四個參數(shù),分別是:
1.源對象(self)
2.關聯(lián)時的用來標記是哪一個屬性的key(因為你可能要添加很多屬性,這里咱們填寫的是&nameKey)
3.關聯(lián)的對象(name)
4.一個關聯(lián)策略(OBJC_ASSOCIATION_COPY_NONATOMIC)。
在匿名分類里面寫上上述代碼后,執(zhí)行Xcode命令command+b,此時匿名分類提示黃色警報
Property 'name' requires method 'name' to be defined - use @dynamic or provide a method implementation in this category
如果運行給分類里面的name屬性賦值(執(zhí)行setter方法)是沒有問題的
NSObject *objc = [[NSObject alloc] init];
objc.name = @"almost";
但是如果獲取name的值,例如執(zhí)行NSLog(@"%@", objc.name);
,那么運行到這里就會crash并提示
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject name]: unrecognized selector sent to instance 0x6000000064a0'
這句話的意思就是你沒有實現(xiàn)name的getter方法,將getter方法在匿名類實現(xiàn)
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
在這里用到了objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
這個方法,這個方法有兩個參數(shù),填寫方法參照setter方法
//利用靜態(tài)變量地址唯一不變的特性
static NSString *nameKey = @"nameKey";
static void *nameKey = &nameKey;
static char nameKey;
關聯(lián)策略是個枚舉值,解釋如下:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, //好比assign關聯(lián)對象的屬性是弱引用,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //好比strong, nonatomic,關聯(lián)對象的屬性是強引用并且關聯(lián)對象不使用原子性,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //好比copy, nonatomic,關聯(lián)對象的屬性是copy并且關聯(lián)對象不使用原子性
OBJC_ASSOCIATION_RETAIN = 01401, //好比strong, atomic,關聯(lián)對象的屬性是copy并且關聯(lián)對象使用原子性
OBJC_ASSOCIATION_COPY = 01403 //好比copy, atomic,關聯(lián)對象的屬性是copy并且關聯(lián)對象使用原子性
};
那么使用runtime給分類添加屬性的全部代碼就是
.h文件
#import <Foundation/Foundation.h>
@interface NSObject (Person)
@property (nonatomic, copy) NSString *name;
@end
.m文件
#import "NSObject+Person.h"
#import <objc/runtime.h> /*或者 #import <objc/message.h>*/
static NSString *nameKey = @"nameKey"; //那么的key
@interface NSObject ()
@end
@implementation NSObject (Person)
/**
setter方法
*/
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
/**
getter方法
*/
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
@end
在其他類的實例種中調用
- (void)viewDidLoad {
NSObject *objc = [[NSObject alloc] init];
objc.name = @"almost";
NSLog(@"%@", objc.name);
}
另附上代碼,點擊代碼地址或點擊鏈接https://github.com/173323222/CategoryProperty