基本反射
基本反射包括小程序
- 获取Class 或 根据字符串获取Class
- 检查是否有selector 以及 根据字符串 获取selector 并执行
- 检查继承关系
基本反射就是能经过NSObject
的一些方法和简单封装好的方法直接能进行反射的操做函数
Class相关的一些操做
首先就是获取一个实例的Class: [self class]
编码
这个就是获取self对应实例的Class类型atom
也能够经过[类名 class]的方式获取Class,好比[UIView class]
和[[[UIView alloc] init] class]
获取到的Class是同样的spa
固然最主要还得有相似Java的Class.forName(String)
经过字符串直接获取到Class : NSClassFromString
.net
好比获取UIView的Class能够 NSClassFromString(@"UIView")
直接返回的就是UIView的Class指针
那么获取到Class有什么用呢?code
- 直接经过Class来实例化对象
- 经过Class 你能够知道Class下面那些方法 属性 和 变量 ,并能够直接访问他们(会在后面的搞基反射里面讲)
经过Class 直接实例化对象 很简单 好比component
Class viewClass = NSClassFromString(@"UIView"); UIView *view = [viewClass alloc] init] ;
能够看到viewClass和UIView是等价的,包括对 +
类型方法的调用也是即 [UIView layerClass]
和[NSClassFromString(@"UIView") layerClass]
是等价的
selector相关
selector对应的就是Java中的Method了 对应Method
这个类 在Objective-C中是SEL
SEL
是一个结构体的指针typedef struct objc_selector *SEL;
SEL
能够经过 @selector
和NSSelectorFromString
来直接获取
而SEL
和Method
的不一样在于 SEL
在Mac系统中是单例的 .
即[Foo count]
和[Bar count]
里面的count 指向的是同一个指针,
包括@selector(count)
和NSSelectorFromString(@"count")
指向的也都是同一个指针
这和Java每一个Class用getMethod
取出的Method都是单独的实例是不一样的
SEL
对应的就是方法的名字 , 这和Objective-C的实现有关,就是方法对应的是消息,而SEL
就是消息的名称,因此不一样的实例可使用相同的消息名,而消息名自己是单例的,不和实例自己产生关系
而后经过- (BOOL)respondsToSelector:(SEL)aSelector
能够判断实例是否真的有对于selector的实现,无论是否有被声明.
而要反射调用一个selector则能够经过一系列的performSelector:
方法进行实现 好比
继承关系
相似Java 的 instanceOf
Objective-C 也有相似的方法,有
- (BOOL)isKindOfClass:(Class)aClass - (BOOL)isMemberOfClass:(Class)aClass + (BOOL)isSubclassOfClass:(Class)aClass - (BOOL)conformsToProtocol:(Protocol *)aProtocol
这几个方法都是定义在NSObject
上的,区别在于
-
isKindOfClass 基本和Java 的
instanceOf
的功能一致 ,而isMemberOfClass 不能识别到父类 只能代表究竟是不是这个Class ,
而isSubclassOfClass是
+
类型的方法和isKindOfClass同样的,不过就是经过Class来进行调用,conformsToProtocol则是识别实例是否符合特定协议
高级反射
高级反射基本就是相似于Java的整个反射体系了,只不过Objective-C的这部分反射都是经过C调用实现的,比起来比较苦逼
主要的一些函数有:
-
objc_msgSend
系列 - class/protocol 系列
- method/SEL/IMP 系列
- ivar /property系列
大部分的调用走包含在
#import <objc/runtime.h> #import <objc/message.h>
这两个头文件里
objc_msgSend
看名字就能知道 这个是objective-c的消息发送函数 ,上一篇也讲到全部的Objective-C的调用全是经过objc_msgSend
来实现的
objc_msgSend的使用仍是比较简单的,看id objc_msgSend(id theReceiver, SEL theSelector, ...)
就能知道.
这里就介绍一些技巧
因为objc_msgSend 返回的是id 那么若是方法定义的是 基本类型怎么办?
看个样例
unsigned retForUnsigned = ((unsigned ( *)(id, SEL)) objc_msgSend)(self, NSSelectorFromString(nsPropertyName));
经过这种cast就能够返回cast为对于的基本类型
而若是返回是浮点的话 能够直接调用double objc_msgSend_fpret(id self, SEL op, …)
那么还有一种状况就是返回的是一个struct的话 须要调用 void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...)
来完成
固然 他们都有对应的super函数来直接调用父类的方法,如objc_msgSendSuper
实际上objc_XXX/object_XXX方法等方法都能找到对于的Objective-C的方法
不过有一个比较有意思的 能够向你们介绍一下
那就是void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
和 id objc_getAssociatedObject(id object, const void *key)
使用这一对函数就能够动态的为对象加getter/setter方法
你们知道使用Categroy是不能直接加property的,可是经过上面一对就能够
能够看AFNetworking中的代码
static char kAFImageRequestOperationObjectKey; @interface UIImageView (_AFNetworking) @property(readwrite, nonatomic, retain, setter = af_setImageRequestOperation:) AFImageRequestOperation *af_imageRequestOperation; @end @implementation UIImageView (_AFNetworking) @dynamic af_imageRequestOperation; @end #pragma mark - @implementation UIImageView (AFNetworking) - (AFHTTPRequestOperation *)af_imageRequestOperation { return (AFHTTPRequestOperation *) objc_getAssociatedObject(self,&kAFImageRequestOperationObjectKey); } - (void)af_setImageRequestOperation:(AFImageRequestOperation *)imageRequestOperation { objc_setAssociatedObject(self, &kAFImageRequestOperationObjectKey,imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
不是设置@synthesize而是设置@dynamic + objc_getAssociatedObject/objc_setAssociatedObject 来完成动态的属性添加
class/protocol
对应的class_XXX和protocol_XXX函数 这里面的方法基本NS都包含了
不过这里咱们看一个声明
struct objc_class { Class isa; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
这是一个objectc class的原始定义 从里面就能看到一个Class 都包含了那些东西哦
method/SEL/IMP
这里说一下概念
Method就是方法 实际上他包含了SEL和IMP 不一样于SEL它是有宿主的,并非单例
SEL在上面已经介绍了实际上他就是等价于方法的名字
而IMP实际就是方法的真正实现了
若是要作动态方法解析 那么就能够本身做IMP来转换SEL对于的实现
ivar /property
ivar就是定义的变量,而property就是属性了
这里要注意的就是取出一个class的ivar/property 用到的相似函数
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
注意到它是copy的,也就是说这块内存是copy 你得本身负责最后去
例子:
unsigned int propertyCount; objc_property_t *pProperty = class_copyPropertyList(class, &propertyCount); if (pProperty && propertyCount > 0) { for (unsigned int i = 0; i < propertyCount; i++) { [self setPropertyToObject:o pProperty:pProperty[i] withDepth:depth AndClass:class]; } } if (pProperty) { free(pProperty); }
不过这里有个比较苦逼的事情就是 去的ivar/property的类型值,这里Objective-C使用属性类型编码来区分类型
因此最后经过const char *property_getAttributes(objc_property_t property)
取到的是一个字符串, 得本身解析这个字符串来取得类型
对于的编码:
属性声明 | 属性描述 |
---|---|
@property char charDefault; | Tc,VcharDefault |
@property double doubleDefault; | Td,VdoubleDefault |
@property enum FooManChu enumDefault; | Ti,VenumDefault |
@property float floatDefault; | Tf,VfloatDefault |
@property int intDefault; | Ti,VintDefault |
@property long longDefault; | Tl,VlongDefault |
@property short shortDefault; | Ts,VshortDefault |
@property signed signedDefault; | Ti,VsignedDefault |
@property struct YorkshireTeaStruct structDefault; | T{YorkshireTeaStruct=”pot”i”lady”c},VstructDefault |
@property YorkshireTeaStructType typedefDefault; | T{YorkshireTeaStruct=”pot”i”lady”c},VtypedefDefault |
@property union MoneyUnion unionDefault; | T(MoneyUnion=”alone”f”down”d),VunionDefault |
@property unsigned unsignedDefault; | TI,VunsignedDefault |
@property int (functionPointerDefault)(char ); | T\^?,VfunctionPointerDefault |
@property id idDefault; Note: the compiler warns: no ‘assign’, ‘retain’, or ‘copy’ attribute is specified - ‘assign’ is assumed” | T@,VidDefault |
@property int intPointer; | T\^i,VintPointer |
@property void voidPointerDefault; | T\^v,VvoidPointerDefault |
@property int intSynthEquals; | In the implementation block: |
@synthesize intSynthEquals=_intSynthEquals; | Ti,V_intSynthEquals |
@property(getter=intGetFoo, setter=intSetFoo:) int intSetterGetter; | Ti,GintGetFoo,SintSetFoo:,VintSetterGetter |
@property(readonly) int intReadonly; | Ti,R,VintReadonly |
@property(getter=isIntReadOnlyGetter, readonly) int intReadonlyGetter; | Ti,R,GisIntReadOnlyGetter |
@property(readwrite) int intReadwrite; | Ti,VintReadwrite |
@property(assign) int intAssign; | Ti,VintAssign |
@property(retain) id idRetain; | T@,&,VidRetain |
@property(copy) id idCopy; | T@,C,VidCopy |
@property(nonatomic) int intNonatomic; | Ti,VintNonatomic |
@property(nonatomic, readonly, copy) id idReadonlyCopyNonatomic; | T@,R,C,VidReadonlyCopyNonatomic |
@property(nonatomic, readonly, retain) id idReadonlyRetainNonatomic; | T@,R,&,VidReadonlyRetainNonatomic |
下面有个小程序用来解析这个属性编码
+ (PropertyAttributeInfo *)analyseProperty:(objc_property_t)pProperty WithClass:(Class)aClass { NSString *propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(pProperty)]; NSMutableString *propertyName = [NSMutableString stringWithUTF8String:property_getName(pProperty)]; PropertyAttributeInfo *info; if ((info = [[PropertyAttributeInfoCache instance] getFromCacheWithClass:aClass AndPropertyName:propertyName]) != nil) { return info; } TypeOfProperty typeOfProperty = NIL; Class class = nil; BOOL readOnly = NO; Class arrayClass = nil; NSString *dicPropertyName = propertyName; NSArray *array = [propertyAttributes componentsSeparatedByString:@","]; NSString *typeAtt = [array objectAtIndex:0]; if ([typeAtt hasPrefix:@"Tc"]) { typeOfProperty = CHAR; } else if ([typeAtt hasPrefix:@"Td"]) { typeOfProperty = DOUBLE; } else if ([typeAtt hasPrefix:@"Ti"]) { typeOfProperty = INT; } else if ([typeAtt hasPrefix:@"Tf"]) { typeOfProperty = FLOAT; } else if ([typeAtt hasPrefix:@"Tl"]) { typeOfProperty = LONG; } else if ([typeAtt hasPrefix:@"Ts"]) { typeOfProperty = SHORT; } else if ([typeAtt hasPrefix:@"T{"]) { typeOfProperty = STRUCT; } else if ([typeAtt hasPrefix:@"TI"]) { typeOfProperty = UNSIGNED; } else if ([typeAtt hasPrefix:@"T^i"]) { typeOfProperty = INT_P; } else if ([typeAtt hasPrefix:@"T^v"]) { typeOfProperty = VOID_P; } else if ([typeAtt hasPrefix:@"T^?"]) { typeOfProperty = BLOCK; } else if ([typeAtt hasPrefix:@"T@"]) { typeOfProperty = ID; if ([typeAtt length] > 4) { class = NSClassFromString([typeAtt substringWithRange:NSMakeRange(3, [typeAtt length] - 4)]); if ([class isSubclassOfClass:[NSArray class]]) { NSUInteger location = [propertyName rangeOfString:@"$"].location; if (location != NSNotFound) { arrayClass = NSClassFromString([propertyName substringWithRange:NSMakeRange(location + 1, [propertyName length] - location - 1)]); dicPropertyName = [NSString stringWithString:[propertyName substringWithRange:NSMakeRange(0, location)]]; } } } } if ([array count] > 2) { for (NSUInteger i = 1; i < [array count] - 1; i++) { NSString *att = [array objectAtIndex:i]; if ([att isEqualToString:@"R"]) { readOnly = YES; } } } info = [[PropertyAttributeInfo alloc] init]; info.readOnly = readOnly; info.class = class; info.type = typeOfProperty; info.arrayClass = arrayClass; info.dicPropertyName = dicPropertyName; info.oriPropertyName = propertyName; [[PropertyAttributeInfoCache instance] putToCacheWithClass:aClass AndPropertyName:propertyName WithInfo:info]; return info; }