Objective-C 反射篇

By 淘寶文通objective-c


基本反射

基本反射包括小程序

  • 獲取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

  1. 直接經過Class來實例化對象
  2. 經過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 能夠經過 @selectorNSSelectorFromString來直接獲取

SELMethod的不一樣在於 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上的,區別在於

  1. isKindOfClass 基本和Java 的 instanceOf的功能一致 ,

    而isMemberOfClass 不能識別到父類 只能代表究竟是不是這個Class ,

    而isSubclassOfClass是+類型的方法和isKindOfClass同樣的,不過就是經過Class來進行調用,

    conformsToProtocol則是識別實例是否符合特定協議

    高級反射

    高級反射基本就是相似於Java的整個反射體系了,只不過Objective-C的這部分反射都是經過C調用實現的,比起來比較苦逼

    主要的一些函數有:

  2. objc_msgSend 系列

  3. class/protocol 系列
  4. method/SEL/IMP 系列
  5. 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,&amp;kAFImageRequestOperationObjectKey);
}

- (void)af_setImageRequestOperation:(AFImageRequestOperation *)imageRequestOperation {
    objc_setAssociatedObject(self, &amp;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, &amp;propertyCount);
 if (pProperty &amp;&amp; propertyCount &gt; 0) {
     for (unsigned int i = 0; i &lt; 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] &gt; 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] &gt; 2) {
    for (NSUInteger i = 1; i &lt; [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;

}
相關文章
相關標籤/搜索