文章目录
  1. 1. Objective-C –反射篇

Objective-C –反射篇

Objective-C –反射篇 (2013-08-21 15:57:36)转载▼

标签: 杂谈     分类: iphone开发

http://zephyrleavesblog.sinaapp.com/?p=676

\

今天我们来讲讲Objective-C的反射,

\

一般Java开发工程师听到Objective-C支持反射一定很兴奋吧!

\

\

\

基本反射

基本反射包括

\

获取Class 或 根据字符串获取Class

检查是否有selector 以及 根据字符串 获取selector 并执行

检查继承关系

基本反射就是能通过NSObject的一些方法和简单封装好的方法直接能进行反射的操作

\

Class相关的一些操作

首先就是获取一个实例的Class: [self class]

\

这个就是获取self对应实例的Class类型

\

也可以通过[类名 class]的方式获取Class,比如[UIView
class]和[[[UIView alloc] init] class]获取到的Class是一样的

\

当然最主要还得有类似Java的Class.forName(String)通过字符串直接获取到Class
NSClassFromString

\

比如获取UIView的Class可以 NSClassFromString( “UIView”)
直接返回的就是UIView的Class

\

那么获取到Class有什么用呢?

\

直接通过Class来实例化对象

通过Class 你可以知道Class下面那些方法 属性 和 变量
,并可以直接访问他们(会在后面的搞基反射里面讲)

通过Class 直接实例化对象 很简单 比如

\

 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

#import

这两个头文件里

\

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 <</span> 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 <</span> [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;

\

}

文章目录
  1. 1. Objective-C –反射篇