objective-c runtime

来源:互联网 发布:咸鱼抱枕淘宝 编辑:程序博客网 时间:2024/04/24 21:10

本文根据苹果文档分析下objective-c runtime;

简介

Objective-c 语言把许多compile、link处理都推迟runtime时处理。它尽可能的动态处理操作。这意味着objective-c不仅需要compiler,更需要一个runtime system去的执行编译后的代码(compiled code).Runtime system的作用就像objective-c语言的一种操作系统(operating system);使得这门语言正常工作。

本文档主要要析下NSObjec class以及Objective-c programs 怎样同runtime system交互.特别是,它探讨了runtime动态加载new class的范例,以及向其它对象转发消息。而且, It provides information about how you can find information about objects while your program is running.

一、怎样同runtime交互

Objective-c程序与runtime system在三个不同的层次上交互:通过objective-c source code ;通过Foundation framework中NSObject class定义的一些method;通过直接调用runtime functions;


1.Objective-c source code

大部分时间,runtime system在后台自动运行。只要你编写并且编译了Objective-C source code,runtime system在后台就自动运行。

当你编译一些包含Objective-C classes and methods的代码时,编译器(compiler)就创建一些实现objective-c语言动态特性的data structures and function calls。这些data structures从class and category definitions and in protocol declarations中捕获相应的信息;这些 data structures also include  在类以及协议的定义中声明的(discussed in “Defining a Class” and “Protocols”)the class and protocol objects,以及一些method selectors,instance variable templates, and other information distilled from source code.

Runtime 的主要功能是消息转发。


2.NSObject Methods

Cocoa中的大部分objects都是NSObject class的子类,因此也继承了NSObject定义的方法(NSProxy除外)。因此,NSObject 定义的methods在它的子类对象或变量中,照样起作用。但在少数情况下,NSObject只定义一个模板for how something should be done;它自身并不实现这些代码。

比如,NSObject class定义了一个description instance method来返回一个描述类信息的string.它主要用于debugging-the GDB print-object command 打印出从该方法返回的string. 该方法NSObject的实现并不知道what the class contains,因此它返回一个name+address of the object的string.NSObject的子类可以实现该方法以返回更多详细信息.比如,NSArray中的description方法就返回该数组中包含的对象的descriptions.

一些NSObject的方法可以简单查询runtime system的信息。这些方法允许objects to perform introspection.比如,class method允许一个object来查询它的类;isKindOfClass:和isMemberOfClass:这两methods来确认一个object在继承树中的位置;respondsToSelector:该方法表明某object是否可以接收某个message;conformsToProtocol:方法表明一个object是否实现了某个protocol中定义的方法;methodForSelector:该方法则提供了某个方法的实现地址;

Methods like these give an object the ability to introspect about itself.


3.Runtime Functions

Runtime system是一个动态的共享库,通过一个公共接口,由位于目录//usr/include/objc内部头文件内一系列functions and data structures组成;

其中的一些方法允许你用c语言去复制当你写objective-c code时,编译器作了什么。其它则构成一些基础功能通过NSObject Method导出(Others form the basis for functionality exported through the methods of the NSObject class)。这些方法使开发者可以开一些runtime system的interface,produce tools that augment(增强) the development environment;当你programming in Objective-C时,并不需要用这些方法。但有一些runtime functions有些情况下在writing an Objective-C programs会很有用,这些方法的定义可以查看 Objective-C Runtime Reference.


二、消息

这章节描述message expressions 是怎样converted into objc_msgSend function calls,以及怎样才能refer to methods by name。然后揭示怎样才能take advantage of objc_msgSend,怎样才能circumvent(规避) dynamic binding。


1、The objc_msgSend Function

在Objective-c中,messages直到runtime才绑定method implementation.

编译器把message expression:

[receiver message];

转换成:a call on a messaging function, objc_msgSend.This Function 把消息表达式中的receiver、the name of the method(the method selector),作为自已的两个主要参数:

objc_msgSend(receiver, selector)

Message expression传来的任何参数,也由objc_msgSend处理:

objc_msgSend(receiver, selector, arg1, arg2, ...)

objc_msgSend函数负责处理dynamic blinding:

  • 首先objc_msgSend根据selector参数找到method implementation程序(procedure)片段.因同样的method可能被不同的class不同的实现,故the precise procedure依赖于the class of the receiver.
  • 然后objc_msgSend调用该方法实现代码(procedure),并传参数:receiver、any arguments that were specified for the method.
  • 最后objc_msgSend把该procedure的结果作为自己的返回结果(return value)返回.

: The compiler generates calls to the messaging function. You should never call it directly in the code you write.


消息转发的关键在于compiler为each class and object生成的structures.每个class structure都包含两个必要的元素:


  • A pointer to the superclass.

  • A class dispatch table.

    该表中的每个条目都把method selector和相应的class-specific addresses of the methods they identify关联在一起. 比如:The selector for the setOrigin:: method is associated with the address of (the procedure that implements) setOrigin::,and so on.

当一个object创建时,memory for it分配,instance variables初始化.Object’s variables中的第一个变量是一个指向它的class structure指针(pointer)。该pointer (isa),使得该object可以访问its class,通过its class,访问到它所继承的所有classes.

: 虽然不是严格语言中的一部分,但对一个object来说,the isa pointer却是必须的,以便和runtime system交互。An object 必须是“equivalent” to a struct objc_object(objc/objc.h)在该structure定义的任何领域.但,你没必要定义自己的root object,因任何NSObject或NSProxy的子类都自动的拥有isa variable.

这些class and object structure's elements如以下插图所示:


当向an object发消息时,objc_msgSend 函数根据该object's isa 指向的class structure,在该class structure的dispatch table中找到对应的method selector.若是找不到,objc_msgSend则根据该class structure's superclass指向的superclass structure的dispatch table中查找对应的method selector.Successive failures导致objc_msgSend沿着该object's class hierarchy继续向上寻找,直至NSObject class.一旦定位到该selector,objc_msgSend函数就通过该selector对应的class-specific addresses of method implementation来调用该实现procedure,并传给它receiving object’s data structure.

这就是runtime时,method implementation被选择的方式.以面向对象编程的术语来说,叫做动态绑定到消息。

为了加速messaging process,runtime system把用过的selectors and the address of methods缓存起来.Each class都有一个单独的cache,它可以缓存the selectors for inherited methods as well as methods defined in the class.在查找dispatch tables之前,objc_msgSend通常先检查the cache of the receiving object’s class.若是该selector在该cache中,messaging 则直接执行上述过程.一旦程序已经运行足够久来“warm up”its caches,那么大部分的消息都可在the cache of the receiving object’s class中找到它的实现.当程序运行后,Caches会动态的增长以容纳新的消息.

2.Using Hidden Arguments

当objc_msgSend找到a method's implementation procedure,它就会调用该procedure并把所有参数传递给它.

当然objc_msgSend也会把两个hidden arguments传值给该procedure:

  • The receiving object

  • The selector for the method

这两参数给每个method implementation关于调用它的消息表达式的明确信息。这些参数说成“hidden”是因为它们在定义该方法的源码中并没有定义.当程序is compiled,它们是被插入到method implementation中的。

虽然这些参数并没有被“explicitly” declared,源代码仍可引用它们(就像引用该receiving object's instance variables).A method使用self引用the receiving object,使用_cmd来引用its own selector.

在如下的例子中,_cmd指的是the selector for the strange method,self 指的是 the object that receives a strange message.

- strange
{
    id  target = getTheReceiver();
    SEL method = getTheMethod();
 
    if ( target == self || method == _cmd )
        return nil;
    return [target performSelector:method];
}

self is the more useful of the two arguments. It is, in fact, the way the receiving object’s instance variables are made available to the method definition.

self的作用不止于此,实际上,它是the receiving object’s instance variables对函数定义(method definition)起作用的方式。

3.Getting a Method Address

规避动态绑定(circumvent dynamic binding)的唯一方法是:获取函数地址(the address of a method)并像调用function一样直接调用.

/*

*注:objectivc-c中,Method和function区别:Functions是一种与objective-c class/object无关的函数块,从c语言继承而来.实际上就是一个标准的c语言函数.

*用法如下:

// declarationint fooFunction() {    return 0;}// callint a;a = fooFunction();

*相对应的,methods则必须和objective-c class/object 关联的方法,且只能通过class/object来调用它们。简言之:methods就是objective-c语言定义的函数.

用法如下:

// declaration- (int)fooMethod {    return 0;}// callint a;a = [someObjectOfThisClass fooMethod];
*/


上述方式在有些罕见情况是比较合适:当某个method将被成功调用很多次,且你想避免每次函数调用时messaging(消息路由)的消耗.

通过NSObject类的methodForSelector:方法,你可以得到一个指某函数实现的指针(a pointer to the procedure that implements a method),然后可以用该指针来调用函数实现程序块(use the pointer to call the procedure).

methodForSelector返回的pointer必须仔细强制(carefully cast to)转换成the proper function type.且,return and argument types 必须包含在该转换中。

如下示例展示setFilled:method的方法实现procedure怎么被调用的:

void (*setter)(id, SEL, BOOL);
int i;
 
setter = (void (*)(id, SEL, BOOL))[target
    methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
    setter(targetList[i], @selector(setFilled:), YES);

传给该Procedure的前两参数是:the receiving object (self)、the method selector (_cmd).这两参数在method syntax(语法)中是隐藏的,但当a method is called as a function时,必须是显式的。

用methodForSelector:来规避动态绑定可以节省objective-c中消息路由的大部分时间.但是,这种方式只有“某个消息被重复很多次”时,才有意义,比如for loop.

注:methodForSelector:是由cocoa runtime system提供,它不是objective-c语言自己的特征.

三、Dynamic Method Resolution

本章节介绍怎样动态的提供一个方法的实现(how to provide an implementation of a method dynamically).

1、Dynamic Method Resolution

有时,你需要动态的提供一个方法的实现(provide an implementation of a method dynamically).比如,Objective-c中的dynamic properties directive:

@dynamic propertyName;

该声明告诉compiler:和该property关联的methods将会动态的提供.

通过实现resolveInstanceMethod: 和resolveClassMethod:方法,对于一个给定的selector for an instance and class method,可以分别动态的提供implementation.

An Objective-c method只是一个至少需要两个参数(self and _cmd)的简单的C function.你可以通过class_addMethod function把一个C function添加为a method for a class.

对于如下给定function:

void dynamicMethodIMP(id self, SEL _cmd) {
    // implementation ....
}

可以用resolveInstanceMethod:动态添加为class method(called resolveThisMethodDynamically) like this:

@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL == @selector(resolveThisMethodDynamically)) {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}
@end

Forwarding methods和Dynamic method resolution很大部分都是交叉的.A class 有机会在进入forwarding mechanism之前dynamically resolve a method.

若respondsToSelector:或instancesRespondToSelector:被调用,the dynamic method resolver有机会提供an IMP for the selector first.

若你实现了resolveInstanceMethod:实际上却想使某些 selectors 通过forwarding mechanism 来转发,你需要return NO for those selectors.

3、Dynamic loading

Objective-c程序运行时可以load and link new classes and categories.就像编译已经编译、link的代码一样,new code is incorporated into the program.

Dynamic loading 可以做很多不同的东西.比如,the System Preferences application中的不同modules都是dynamically loaded.

Cocoa environment中,dynamic loading平常用来allow applications to be customized.其他人写的一些模块,你可以像Interface Builder loads custom palettes and the OS X System Preferences application loads custom preference modules一样,在runtime加载这它们.The loadable modules可以扩展what your application can do.你提供framework,其他人则使用它来write code.

虽然Mach-O文件中,已经有一个 runtime function(objc_loadModules,defined in objc/objc-load.h) 来处理dynamic loading of Objective-C modules,Cocoa's NSBundle class也提供了一个更方便的interface来处理dynamic loading-one that’s object-oriented and integrated with related services. 在Foundation framework reference中的NSBundle class 可以查看它的特点及用法.在OS X ABI Mach-O File Format Reference中可以查找关于Mach-O files的信息.

四、Message Forwarding

Sending a message 给一个不能处理该消息的object (that does not handle that message) 会抛出error.但是,在抛出error之前,runtime system会给receiving object一个机会去处理该消息(handle the message).

1、Forwarding

若send a message to an object that does not handle that message,在发出error之前,runtime system会给receiving object发送forwardInvocation: message with an NSInvocation object作为自己唯一的参数.NSInvocation object封闭了原始的消息及参数信息(the original message and the arguments).

你可以实现forwardInvocation:方法给该消息设定一个默认的响应(a default response to the message),或者用其它的方式来avoid this error.forwardInvocation:平常用以forward the message to another object.

为了看到 forwarding 的范围及意图,想象以下场景:首先,假定你设计了一个可以响应(response to)negotiate消息的object,且你希望its response包含another kind of object's response.你可以简单的实现这项功能,通过在该negotiate方法的实现中,passing a negotiate message to the other object.

更进一步,假定你希望your object’s response to a negotiate message是一个response implemented in another class.一种实现方式是make your class inherit the method from the other class.但不太可能这样做.很好的理由是:因为your class and the class that implements negotiate在继承树中的不同分枝(There may be good reasons why your class and the class that implements negotiate  are in different branches of the inheritance hierarchy.).

虽然your class不能直接继承negotiate method,但你可以通过class's method implementation发送一个message to an instance of the other class:

- (id)negotiate
{
    if ( [someOtherObject respondsTo:@selector(negotiate)] )
        return [someOtherObject negotiate];
    return self;
}

这种方法有点笨,特别是有一组消息这样处理时.你得在每个方法的实现中像上面一样调用另一个class's method.而且,若在写代码时,你不知道the full set of messages you might want to forward时,这种方法不能处理.因为the full set of messages you might want to forward取决于runtime's events,且它会变动当as new methods and classes are implemented in the future.

forwardInvocation: method对于上述问题提供了一个特别的解决方案,且该解决方案是动态的。

它的工作流程:当an object因don't have a method matching the selector in a message而无法response to the message时,runtime system会通过发送forwardInvocation: message通知它. Every object 都从NSObject class继承一个 forwardInvocation: method.但是NSObject's version of the method 只是简单调用doesNotRecognizeSelector:方法.通过自己overriding and implementing NSObject'sforwardInvocation: 方法,就有机会利用forwardInvocation: message来给other objects转发消息(forward messages).

为了能forward a message,forwardInvocation: method仅需这样做:

  • Determine where the message should go, and

  • Send it there with its original arguments.

可以通过invokeWithTarget: method来发送消息:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];
}

The forwarded messages's return value被返回给the original sender.所有类型的return values都可以传递给the sender,包括ids,structures,double-precision floating-point numbers等.

AforwardInvocation: method 的作用就像是a distribution center for unrecognized messages,把它们实时分发给不同的receivers.或者它可以是一个中转站(transfer station),发送所有的消息到相同的目的地(sending all messages to the same destination).它可以translate one message into another,也可以简单的“吞下”一些消息以致于该消息has no response and no error.

forwardInvocation: method 也可以把合并several messages到只有a single response.具体forwardInvocation:究竟做些什么取决于它的实现.但是,The opportunity it provides for linking objects in a forwarding chain,为program design提供了可能性.

注:只有当invoke an not existing method in the nominal receiver时,the forwardInvocation:method才会处理这些信息.比如,若你希望 your object forward negotiate message to another object,your object自身不能有 negotiate method.若有,该消息将不会到达forwardInvocation:.

查看更多关于forwarding and invocations,你可以去NSInvocation class文档中查看.

2、消息转发和多继承(Forwarding and Multiple Inheritance)

Forwarding模拟inheritance,可以为objective-c programs模拟出一些multiple inheritance的效果.如下图:an object通过forwardInvocation:转发消息,就像“inherit” a method implementation in another class.


在这个演示中,Warrior class's instance转发一个negotiate消息给 一个 Diplomat class's instance.看起来Warrior class's instance响应了negotiate消息,实际上是Diplomat class's instance响应,而Warrior class's instance转发了该响应的结果.

An object通过 forwards a message,从而"inherit" methods from two branches of the inheritance hierarchy:它自己的继承树以及响应消息的object's继承树.上述示例中,Warrior class 就像是 inherit 了Diplomat以及自己的superclass.

Forwarding提供了典型的 multiple inheritance的特征。但,他们之间有很大的差别:Multiple inheritance 结合了其继承的父类的各种不同特性于 a single object;它倾向于大型的、多方位的objects.而 Forwarding 则是assigns separate responsibilities to disparate objects.它把问题分解给更小的objects,但通过一种对于 message sender 透明的方式来关联 those objects.

3、代理对象(Surrogate Objects)

Forwarding 不仅模拟了 multiple inheritance,它也使得可能去开发一些 represent or “cover” more substantial objects 的 lightweight objects.代理(the surrogate)代表了the other object 以及发送给它的funnelsmessage.

The proxy 就是这样一种 surrogate. A proxy 负责 forwarding messages to a remote receiver 的管理细节,确保 argument values 的复制、连接的检索等等.但它并不试图做其它的什么功能;它不重复remote object 的functionality,只是简单的给 remote object 一个local address,该 address 可以 receive messages in another application.

其它种类的 surrogate objects 也是可能的.比如,假定你有一个 object that manipulates a lot of data ,它可能创建了 a complicated image 或者 reads the contents of a file on disk. 设置该object会很费时( time-consuming ),最好 do it lazily,当你需要它时,或者system resources 暂时 idle.同时,你至少需要 a placeholder for this object 以便其它objects in the application 能够 function properly.

在这种情况下,你可以 initially create a lightweight surrogate for it,而非完全加载式的创建该object. This object 可以自己 do some things,比如请求数据,但大多数情况下,它仅仅 hold a place for the larger object,并在合适的时候,转发消息给它(forward messages to it) .当该 surrogate'sforwardInvocation: method 最初接到a message destined for the other object ,它得确保 "that object" 是存在,若不存在则创建. 

所有发送给 the larger object 的 messages 都通过 the surrogate,因此,the surrogate 和 the larger object 通常被作是同一个概念.

3、消息转发和继承 (Forwarding and Inheritance)

虽说 forwarding 可模拟 Inheritance,但 NSObject 决不会混淆两者.respondsToSelector: isKindOfClass: 这两方法只会在 inheritance hierarchy中查找,决不会在 forwarding chain中查找.比如,若要确定 a Warrior object 是否response to anegotiate message,

if ( [aWarrior respondsToSelector:@selector(negotiate)] )
    ...

返回结果是 NO ,即使它可以正常接收 negotiate messages 并 response to them,通过 forwarding them to a Diplomat.

很多情况下,NO 是很合适. 但有时,它不是.若你用 forwarding 来设置 a surrogate object 或者 来扩展 the capabilities of a class,the forwarding mechanism 就像 inheritance 一样 透明 (transparent). 若你希望 your objects 就像真的 inherit the behavior of the objects they forward messages to ,你需要重新实现respondsToSelector: 和 isKindOfClass: 方法来包含你的 forwarding algorithm(算法):

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ( [super respondsToSelector:aSelector] )
        return YES;
    else {
        /* Here, test whether the aSelector message can     *
         * be forwarded to another object and whether that  *
         * object can respond to it. Return YES if it can.  */
    }
    return NO;
}

除了 respondsToSelector: 和 isKindOfClass: 方法,instancesRespondToSelector: method 也需要反映 the forwarding algorithm. 若是实现了某些protocols,conformsToProtocol: method 同样也应当反映 the forwarding algorithm . 同样,若一个 object 转发了任何它收到的 remote messages,那它需要实现 method 以便返回最终响应 the forwarded messages 的方法的准确描述.比如,若 an object forward a message to its surrogate,你得像下面一样实现methodSignatureForSelector: 方法:

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
       signature = [surrogate methodSignatureForSelector:selector];
    }
    return signature;
}

你可以考虑把 the forwarding algorithm 放在某处 private code ,比如method,以便其它的forwardInvocation:等方法来 include(引用),调用它.

注: 这是一种 advanced technique,只适合一些没有别的可能的解决方法(solution)的情形(situations).它并不希望作为一个多继承的替代品.若你必须要用到this technique时,必须确保你已经完全理解了 the behavior of the class doing the forwarding以及 the class you're forwarding to .

这一小节中提到的一些方法都在 NSObject class的声明中.在 NSInvocation class的声明中,可以查看更多关于invokeWithTarget: 的信息.


五、Type Encodings

为辅助runtime system,the compiler 把 the return and argument types for each method 编码成(encodes in) a character string,并把该string和对应的method selector 关联起来. 编码方式在其它一些情况下也有用,因此该编码方式用 @encode() compiler directive来实现.给定某个格式的type时,@encode()返回该type 编码后的 string . 该type可以是基础类型比如 an int, an pointer,a tagged structure or union,or a class name等等,实际上,任何可以用作 Csizeof() operator 的参数任何type,都可以.

char *buf1 = @encode(int **);
char *buf2 = @encode(struct key);
char *buf3 = @encode(Rectangle);

下面的这张表列出了 the type codes .请注意,当你为了 archiving or distribution 而 encoding an object 时,你所用的codes,和其中的很多都有交叉.但是,下面列出的codes,当你 write a coder时,其中的一些你不能用.当然,当你 write a coder 时,也有一些你需要用的codes,不是由@encode() 生成. (想要查看更多关于 encoding objects for archiving or distribution 的信息,可以查看 NSCoder class specification).

Table 6-1  Objective-C type encodings

Code

Meaning

c

char

i

An int

s

short

l

long

l is treated as a 32-bit quantity on 64-bit programs.

q

long long

C

An unsigned char

I

An unsigned int

S

An unsigned short

L

An unsigned long

Q

An unsigned long long

f

float

d

double

B

A C++ bool or a C99 _Bool

v

void

*

A character string (char *)

@

An object (whether statically typed or typed id)

#

A class object (Class)

:

A method selector (SEL)

[array type]

An array

{name=type...}

A structure

(name=type...)

A union

bnum

A bit field of num bits

^type

A pointer to type

?

An unknown type (among other things, this code is used for function pointers)

重要提示: Objective-C 语言不支持 long double type. @encode(long double) returns d, 和@encode(double)编码一样 .

An array's type code 被包含在一对方括号之间; the number of elements in the array 被放在第一个方括号之后,array type之前.比如,an array of 12 pointers to floats 编码如下:

[12^f]

Structures's type code被包含在一对大括号之间;unions则是括号之间. 大括号之中首先列出 the structure tag ,紧接着一个等号(equal sign),接下来则是该 structure 的每个参数字段的编码(codes for the fields of the structure listed in sequence).比如,如下结构体

typedef struct example {
    id   anObject;
    char *aString;
    int  anInt;
} Example;

将会这样编码:

{example=@*i}

无论是把 the defined type name (Example)还是 the structure tag (example) 传值给 @encode() 编码结果一样. A structure pointer's encoding 和structure编码需要一样的内部参数信息,如下:

^{example=@*i}

但是,A structure pointer's pointer则要去除内部的类型信息,即参数信息:

^^{example}

Objects's type code 的处理和 structures 一样.比如,把 NSObject class name传递给  @encode() 编译器指令后,得到如下结果:

{NSObject=#}

The NSObject class 仅仅声明了一个 isa instance variable of type class.

注: 虽然 @encode() 指令没有返回,但the runtime system 使用另外的encodings for type qualifiers 用来声明 methods in a protocol,如下图所示:

Objective-c method encodings:

Code

Meaning

r

const

n

in

N

inout

o

out

O

bycopy

R

byref

V

oneway

六、Properties的声明 (Declared properties)

当compiler遇到property declarations时(见 “Declared Properties” in The objective-c programming language ),它会生成一个与 enclosing class,category or protocol关联的描述性元数据(descriptive metadata). 可以通过一些特定的functions来查看该metadata,这些functions 支持通过name查找a property on a class or protocol,获取作为 an @encode  string 的 property type,拷贝作为 an array of C strings 的 property's attributes list. A list of declared properties 可以用于 each class and protocol.

1、Property 类型和Functions (Property Type and Functions)

Property structure 定义了一个指向 a property descriptor的不透明的句柄,或者说一个指向property descriptor("不透明",因文档中没给出其定义)的指针.
typedef struct objc_property *Property;

可以用 class_copyPropertyList 和 protocol_copyPropertyList 这两个function分别取出一个与 a class (including loaded categories) and a protocol 关联的 properties array:

objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)

比如,给定以下class declaration:

@interface Lender : NSObject {
    float alone;
}
@property float alone;
@end

你可以用如下方法取出 the list of properties:

id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);

可以用 property_getName function 来发现a property's name:

const char *property_getName(objc_property_t property)

对于一个给定的property's name,可以用 class_getProperty 和 protocol_getProperty 这两个functions来分别获取 a class and protocol 的相应的 property's reference:

objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)

可以用 property_getAttributes function来发现 the name and the @encode type string of a property.关于 encoding type strings的详情,见“Type Encodings”;关于this string的详情,见“Property Type String”和“Property Attribute Description Examples”.

const char *property_getAttributes(objc_property_t property)

综合上面,可以用如下代码打印出 a class's properties list:

id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
    objc_property_t property = properties[i];
    fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
2、Property Type String

可以用 property_getAttributes function 来查寻 a property's name、 @encode type string,以及 the property's other attributes.

The property type encoding string,以 T 开头,紧跟着  the @encode type 和一个逗号,结尾以 V 开头,紧跟着 the name of the backing instance variable.
它们之间,the attributes 由以下描述符(descriptors)指定,以逗号分开:
Declared property type encodings

Code

Meaning

R

The property is read-only (readonly).

C

The property is a copy of the value last assigned (copy).

&

The property is a reference to the value last assigned (retain).

N

The property is non-atomic (nonatomic).

G<name>

The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,).

S<name>

The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,).

D

The property is dynamic (@dynamic).

W

The property is a weak reference (__weak).

P

The property is eligible() for garbage collection.

t<encoding>

Specifies the type using old-style encoding.

在"Property Attribute Description Examples",可以看到一些例子.
3、Property Attribute Description 示例

给定如下定义:

enum FooManChu { FOO, MAN, CHU };
struct YorkshireTeaStruct { int pot; char lady; };
typedef struct YorkshireTeaStruct YorkshireTeaStructType;
union MoneyUnion { float alone; double down; };

下表展示了一些property declarations样品以及通过property_getAttributes 返回的相应的string:

Property declaration

Property description

@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

费时好久,终于把这块内容给翻译完毕,收获多多..

0 0
原创粉丝点击