基本反射
基本反射包括小程序
- 獲取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; }