- BSClassInfo.h文件
//
// ClassInfo
// BSClassInfo
// Created by yuYue on 2019/2/2.
// Copyright © 2019 yuYue. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@interface BSIvarInfo : NSObject
@property (assign, nonatomic, readonly) Ivar ivar;
@property (copy, nonatomic, readonly) NSString *name;
@property (assign, nonatomic, readonly) ptrdiff_t offset;
@property (copy, nonatomic, readonly) NSString *typeEncoding;
- (instancetype)initWithIvar:(Ivar)ivar;
@end
@interface BSMethodInfo : NSObject
@property (assign, nonatomic, readonly) Method method;
@property (copy, nonatomic, readonly) NSString *name;
@property (assign, nonatomic, readonly) SEL sel;
@property (assign, nonatomic, readonly) IMP imp;
@property (copy, nonatomic, readonly) NSString *typeEncoding;
@property (copy, nonatomic, readonly) NSString *returnTypeEncoding;
@property (copy, nonatomic, readonly) NSArray<NSString *> *argumentTypeEncoding;
- (instancetype)initWithMethod:(Method)method;
@end
@interface BSPropertyInfo : NSObject
@property (assign, nonatomic, readonly) objc_property_t property;
@property (copy, nonatomic, readonly) NSString *name;
@property (copy, nonatomic, readonly) NSString *typeEncoding;
@property (copy, nonatomic, readonly) NSString *ivarName;
@property (assign, nonatomic, readonly) SEL getter;
@property (assign, nonatomic, readonly) SEL setter;
@property (nullable, assign, nonatomic, readonly) Class cls;
@property (nullable, copy, nonatomic, readonly) NSArray<NSString *> *protocols;
- (instancetype)initWithProperty:(objc_property_t)property;
@end
@interface BSClassInfo : NSObject
@property (assign, nonatomic, readonly) Class cls;
@property (assign, nonatomic, readonly) Class supCls;
@property (assign, nonatomic, readonly) Class metaCls;
@property (assign, nonatomic, readonly) BOOL isMeta;
@property (copy, nonatomic, readonly) NSString *name;
@property (nullable, strong, nonatomic, readonly) BSClassInfo *superClassInfo;
@property (nullable, strong, nonatomic, readonly) NSDictionary<NSString *, BSIvarInfo *> *ivarInfos;
@property (nullable, strong, nonatomic, readonly) NSDictionary<NSString *, BSMethodInfo *> *methodInfos;
@property (nullable, strong, nonatomic, readonly) NSDictionary<NSString *, BSPropertyInfo *> *propertyInfos;
- (instancetype)initWithClass:(Class)cls;
@end
NS_ASSUME_NONNULL_END
複製代碼
- BSClassInfo.m 文件
//
// NSObject+BSClassInfo.m
// ClassInfo
//
// Created by yuYue on 2019/2/2.
// Copyright © 2019 yuYue. All rights reserved.
//
@implementation BSIvarInfo
- (instancetype)initWithIvar:(Ivar)ivar{
if (!ivar) return nil;
self = [super init];
_ivar = ivar;
const char * name = ivar_getName(ivar);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
_offset = ivar_getOffset(ivar);
const char * typeEncoding = ivar_getTypeEncoding(ivar);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
}
return self;
}
@end
@implementation BSMethodInfo
- (instancetype)initWithMethod:(Method)method{
if(!method) return nil;
self = [super init];
_method = method;
_sel = method_getName(method);
const char *name = sel_getName(_sel);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
_imp = method_getImplementation(method);
const char *typeEncoding = method_getTypeEncoding(method);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
}
char *returnType = method_copyReturnType(method);
if (returnType) {
_returnTypeEncoding = [NSString stringWithUTF8String:returnType];
free(returnType);
}
unsigned int argumentCount = method_getNumberOfArguments(method);
if (argumentCount > 0) {
NSMutableArray *arrM = [NSMutableArray array];
for (int i = 0; i < argumentCount; i++) {
char *argumentType = method_copyArgumentType(method, i);
NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : @"";
[arrM addObject:type];
free(argumentType);
}
_argumentTypeEncoding = arrM;
}
return self;
}
@end
@implementation BSPropertyInfo
- (instancetype)initWithProperty:(objc_property_t)property{
if(!property) return nil;
self = [super init];
_property = property;
const char *name = property_getName(property);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
if (_name) {
_getter = NSSelectorFromString(_name);
NSString *setter = [NSString stringWithFormat:@"set%@%@",[[_name substringToIndex:1]uppercaseString],[_name substringFromIndex:1]];
_setter = NSSelectorFromString(setter);
}
unsigned int count;
objc_property_attribute_t *attributs = property_copyAttributeList(property, &count);
for (int i = 0; i < count; i++) {
switch (attributs[i].name[0]) {
case 'T':
{
//typeEncoding
_typeEncoding = [NSString stringWithUTF8String:attributs[i].value];
if (*_typeEncoding.UTF8String == '@') {
size_t len = strlen(_typeEncoding.UTF8String);
if (len != 2) {
//是對象
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
if (![scanner scanString:@"@\"" intoString:NULL]) continue;
NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
_cls = NSClassFromString(clsName);
}
//protocol
NSMutableArray *arrM = [NSMutableArray array];
while ([scanner scanString:@"<" intoString:NULL]) {
NSString *protocol;
if ([scanner scanUpToString:@">" intoString:&protocol]) {
if (protocol.length) {
[arrM addObject:protocol];
}
}
[scanner scanString:@">" intoString:NULL];
}
_protocols = arrM;
}
}
}
break;
case 'V':
{
if (attributs[i].value) {
_ivarName = [NSString stringWithUTF8String:attributs[i].value];
}
}
break;
default:
break;
}
}
return self;
}
@end
@implementation BSClassInfo
- (instancetype)initWithClass:(Class)cls{
if(!cls) return nil;
self = [super init];
_cls = cls;
_supCls = class_getSuperclass(cls);
_isMeta = class_isMetaClass(cls);
if (!_isMeta) {
_metaCls = objc_getMetaClass(class_getName(cls));
}
_name = [NSString stringWithUTF8String:class_getName(cls)];
[self update];
_superClassInfo = [[BSClassInfo alloc] initWithClass:_supCls];
return self;
}
- (void)update{
_ivarInfos = nil;
_methodInfos = nil;
_propertyInfos = nil;
//ivar
Class cls = self.cls;
unsigned int ivarCount = 0;
NSMutableDictionary *dictMIvar = [NSMutableDictionary dictionary];
Ivar *ivarList = class_copyIvarList(cls, &ivarCount);
for (int i = 0; i < ivarCount; i++) {
BSIvarInfo *ivarInfo = [[BSIvarInfo alloc] initWithIvar:ivarList[i]];
[dictMIvar setObject:ivarInfo forKey:ivarInfo.name];
}
_ivarInfos = dictMIvar;
free(ivarList);
//method
NSMutableDictionary *dictMMethod = [NSMutableDictionary dictionary];
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(cls, &methodCount);
for (int i = 0 ; i < methodCount; i++) {
BSMethodInfo *methodInfo = [[BSMethodInfo alloc] initWithMethod:methodList[i]];
[dictMMethod setObject:methodInfo forKey:methodInfo.name];
}
_methodInfos = dictMMethod;
free(methodList);
//property
NSMutableDictionary *dictMProperty = [NSMutableDictionary dictionary];
unsigned int propertyCount = 0;
objc_property_t *propertyList = class_copyPropertyList(cls, &propertyCount);
for (int i = 0; i < propertyCount; i++) {
BSPropertyInfo *propertyInfo = [[BSPropertyInfo alloc] initWithProperty:propertyList[i]];
[dictMProperty setObject:propertyInfo forKey:propertyInfo.name];
}
_propertyInfos = dictMProperty;
free(propertyList);
}
@end
複製代碼
- 總結 以上代碼是讀完YYModel中的YYClassInfo源碼學到的利用runtime獲取類的信息。YYClassInfo源代碼中有關於緩存的部分,而咱們的目的是抽取runtime部分,因此這裏沒有作緩存。有興趣的小夥伴能夠和YYClassInfo源代碼作下對比會發現有些部分會有些少量改動。其實就是按照本身的習慣寫出來的代碼會和源代碼有些不一樣,但都達到了獲取類信息的目的。該代碼有助於理解YYModel源碼以及能夠進一步學習runtime知識,一箭雙鵰。