若是读到感受不理解、晦涩的地方,或者想要交流的能够联系我QQ1325582826,Call me!欢迎赐教!html
Objective-C语言尽量多的将许多决定从编译链接推迟到运行时。不管什么时候,它都尽量的动态处理事件。这就意味着OC语言不只仅须要编译器,还须要一个运行时系统来执行编译完成的代码。对于OC而言,运行时系统扮演了操做系统的角色;就是它使得OC运行起来。编程
这个文档涉及到NSObject类和Objective-C程序如何与运行时系统互相做用。尤为是,对于动态加载新的类和向其余对象转发消息,本文档可用于检索编程示例。咱们也能够从本文档查到在程序运行时,关于如何查找到对象相关的信息。数组
咱们应该阅读此文档,以便加深(对OC运行时系统是如何工做的和如何利用它)的认知和理解。尤为是,咱们在写Cocoa APP时,有必要阅读这份文档。缓存
本文档有一下章节:数据结构
Objective-C Runtime Reference描述了OC运行时库支持的数据结构和函数。咱们变成可使用这些接口和OC运行时系统交互。例如,咱们能够添加类和方法,或者获取全部(已经加载的)类的定义的列表。
Programming with Objective-C 描述了OC语言。
Objective-C Release Notes 描述了OSX中,OC运行时在最近实现的变化。app
在不一样的平台,有不一样版本的OC runtime。ide
有两个版本的OC runtime——“旧版”和“如今版”。如今版就是OC-2.0并包含了许多新特性。旧版本的runtime的编程接口就是OC-1;如今版本的runtime所有接口参见 Objective-C Runtime Reference 。
最值得注意的新特性是,如今版本的实例变量是“不脆弱的”:函数
另外,如今版本的runtime支持为声明的属性作实例变量的synthesis(参见Objective-C Programming Language)。工具
iPhone应用和OSX 10.5版本的64-位编程使用如今版本的runtime。布局
OC编程和runtime系统的相互做用,能够分三个不一样的标准:
这是最重要的一部分,runtime 系统在该场景背后自动运行。咱们仅仅经过写和编译OC代码就可使用runtime系统。
当编译包含OC类和方法的代码时,编译器就会建立数据结构和(实现了语言动态特征)函数。数据结构可以捕获有Class和category以及protocol中声明的信息;它们包含了Class和Protocol(在Objective-C Programming Language 中定义的Class和Protocol,还有方法selectors、实例变量以及其余从源码中提取到的信息)。主要的runtime功能就是发送消息,参见 Messaging ,它也会被OC代码消息表达式调用。
许多Cocoa种的对象都是NSObject类的子类,所以许多对象继承了它定义的方法。(NSProxy类是个例外,更多信息参见 Message Forwarding 。)所以它的方法创建了行为(对每一个实例和类对象来讲都是已经存在的方法实现)。少数状况下,NSObject类只定义了应该如何作的方法模板,它自身不提供全部的必须的代码。
例如,NSObject类定义了description实例方法,该方法用于返回一个用于描述类内容的字符串,这主要是用于debugging—GDB print-object命令打印由该方法返回额字符串。NSObject的该方法的实现不知道该类包含什么,所以它返回一个包含了对象的名称和地址的字符串。NSObject的子类可以重写该方法并返回更详细的描述。例如,Foundation的NSArray类返回了一个array包含的全部的对象的列表。
NSObject的一些方法仅仅查询runtime系统获取信息。这些方法使得对象可以执行校验。例如“class”方法,是用来查询对象的类型;isKindOfClass:和isMemberOfClass:,是测试对象在继承层次中的位置;respondsToSelector:,用于校验对象可否接收一个指定的消息;conformsToProtocol:,用于校验是否某个对象声明了指定Protocol中定义的方法的实现;methodForSelector:,用于提供方法实现体的地址。这些对象自己都是校验性的能力的方法。
运行时系统是动态共享库,而且头文件中(文件路径/usr/include/objc)有一系列函数和数据结构接口声明;其中大部分函数容许咱们使用基本的C来复制那些编译器的实现(同咱们以OC代码编译后的代码)。其余基础功能能够经过NSObject得到。这些功能可以让咱们为runtime 系统开发其接口和工具,以便提升开发效率;在使用OC编程时也能够不使用runtime接口。不过,当用OC编写程序时,有些runtime函数功能在某些场合是很是有用的。全部runtime函数声明可参见Objective-C Runtime Reference。
本节讲述消息表达式是如何转换为objc_msgSend函数调用的,和如何经过name查找方法;而后会解释咱们如何充分利用objc_msgSend,和如何避免动态绑定(若是有须要)。
OC中,在运行时前,消息是不能肯定方法的实现体地址的。编译器转换消息表达式,
[receiver message]
转换成消息函数,objc_msgSend。这个函数须要该消息表达式中的接收者(receiver)和方法的名字——方法的selector做为第二个主要的参数:
objc_msgSend(receiver, selector)
任何传入消息的参数都和一经过objc_msgSend处理:
objc_msgSend(receiver, selector, arg1, arg2, ...)
消息函数为动态绑定完成全部所须要的事情:
注意:编译器生成消息函数的调用。咱们永远不要在本身写的代码中直接调用它(PS:文档中此处所说的注意在现实中,貌似只起到了提醒你们要确保消息发送正确的做用,慎重使用)。
消息发送的核心在于编译器为每一个类个对象建立的结构体,每一个类结构体包含两个基本的要素:
注意:该语言有不严谨的一部分,isa指针是对象关联OC运行时系统所必须的。一个对象须要“等价”于一个结构体 objc_object (在objc/objc.h中定义的) ,包含全部该结构体中的份量。然而,咱们不多建立咱们本身的根类(root object),继承自NSObject或者NSProxy的对象会自动具有isa变量。
Class和对象结构体拥有的基本元素如图3-1所示:
当一个消息被发送到某对象,这个消息函数查找指向该类结构体的isa指针,在类结构体中查找到方法的分发表里的对应的selector;若是在此处找不到selector,objc_msgSend 顺着superclass的指针尝试在superclass中的分发表中查找selector。若是尚未查找到,那么objc_msgSend将会沿着类的继承层次向上寻找,直到NSObject类。一旦定位到selector,函数将会调用分发表里的方法,并将reciver 对象的数据结构体传递给它。
这就是方法实现体在运行时选择的方式,以面向对象编程的术语说,methods(方法实现)就是动态绑定到message(消息)。
为了加速消息发送的过程,runtime系统缓存selector和methods被使用的地址。每一个类都有单独 的cache,它能够包含继承的方法的selector,也会包含在该类中定义的方法。在搜索分发表以前,消息机制会先检查receiver 对象类的cache(理论上,被使用过一次的method颇有可能被再次调用);若是selector是在cache里面的,消息发送就只是稍微慢于函数调用。一旦程序运行了足够长时间使得它的类的caches“彻底活跃”,基本上消息发送就是经过查找cache里的method完成了。为了容纳新的消息发送,Caches随着程序的运行动态增加。
当objc_msgSend找到method的实现体时,他就会调用实现体,并将消息中的全部参数传递给他,其中也包含如下两个参数:
Method的实现体将会从消息表达式中得到这两个参数。这两个参数被称为“隐藏的”,是由于它们没有在method中的源码中声明。它们在编译的时候被插入实现体。
尽管这两个参数是隐式声明的,源代码仍旧可以引用他们(因为它可以指向receiver 对象的实例变量)。一个method引用receiver对象就是self(对象自己),对于它本身的selector就是_cmd。下面的例子中,_cmd指向strange方法的selector,self指向接受strange消息的对象。
- strange { id target = getTheReceiver(); SEL method = getTheMethod(); if ( target == self || method == _cmd ) return nil; return [target performSelector:method]; }
self是这两个参数中最有用的。本质上来讲,就意味着,receiver对象的实例变量对于method是可用的。
惟一避免动态绑定的方式就是得到method的地址并直接把它当作一个函数调用。若是某个method须要连续的调用屡次,而且咱们想要避免以前每次method被执行时的消息发送环节,此时避免动态绑定是有必要的。
经过使用NSObject类的methodForSelector:方法,咱们能够查找method的实现体的指针,而后使用这个指针调用实现体。methodForSelector:方法返回必须准确的对应相应的函数类型。参数类型和返回值类型都应该在调用中包含。
下面的例子展现了如何生成setFilled:方法被调用的实现体:
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);
传递到函数的前两个参数是receiver对象(self)和method selector(_cmd)。这些参数是隐藏在method语法中,可是当method被做为函数调用时是必需要显示传递的。
使用methodForSelector:可以避免动态绑定,并节省消息发送机制所须要的时间。然而,仅仅当执行要被重复屡次的特殊的消息(就像上面的for循环展现同样)这种节省时间的才有意义。
注意methodForSelecotor:是被Cocoa runtime系统提供的,而不是OC语言的特征。
本节主要讲如何为method提供动态的IMP(实现)。
有时候咱们须要为method动态地提供IMP。例如OC声明属性特征(Declared Properties in The Objective-C Programming Language)包括 @dynmaic 指令:
@dynamic propertyName;
这将会告诉编译器,这个属性关联的methods将会动态提供。
咱们可以经过实现methodsresolveInstanceMethod:和resolveClassMethod:来分别为实例或者类method指定的selector提供IMP。
一个OC的method就是简单的C函数,只不过这个C函数至少有两个参数——self和_cmd。咱们可以使用函数class_addMethod为一个类添加一个函数。例以下面的函数:
void dynamicMethodIMP(id self, SEL _cmd) { // implementation .... }
咱们可以动态的为一个类添加一个method(调用resolveThisMethodDynamically)使用resolveInstanceMethod:以下:
@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
转发methods(就像Message Forwarding中介绍的)和动态方法的原理基本是同样的。一个Class在转发机制前仍是有方法动态处理method的。若是respondsToSelector:或者instancesRespondToSelector是被调用的,此时,动态method是被给一次机会为selector提供IMP。若是咱们实现resolveInstanceMethod:,可是想要某个特殊方法继续消息转发机制,咱们应该为这些selector返回NO。
一个OC程序可以在运行的时候,加载和链接新的类和分类。新的代码是被合并到程序内并和最初加载的类和分类同等对待。
动态加载可以被用于作许多不一样的事情。例如,系统设置APP里的不一样的模块是被动态加载的。
在Cocoa环境中,动态加载一般被用于让APP更个性化。咱们程序可以在运行时加载第三方写的模块——尽管Interface Buider加载自定义的工具,以及OSX偏好设置APP加载自定义的偏好模块。可加载的模块扩展了咱们APP的功能,它们在通过APP容许的状况下才起做用,可是也有难以预测的问题。
尽管,有一个runtime功能是为在Mach-O文件中的OC模块执行动态加载(objc_loadModules,在objc/objc-load.h中定义),Cocoa的NSBundle类为动态加载提供大量的更便利的接口——面向对象并集成了相关服务。经过查看NSBundle类在Foundation framework中的说明,查找NSBundle类和它的使用。相应的对于Mach-O文件查看OSX ABI Mach-O 文件格式参考资料。
将消息发送给一个对象,而且对象没有处理这个消息,就会产生一个错误。可是,在宣告错误以前,运行时系统给receiver 对象第二次机会去处理消息。
若是将消息发送给一个object,而且object没有处理这个消息,在宣告错误以前runtime将forwardInvocation:消息和惟一一个参数即NSInvocation对象发送给object;NSInvocation对象封装了最初的消息和传递过来的参数。
咱们可以实现forwardInvocation:方法为消息提供一个默认的相应,或者以某种方式避免这种错误。见名知意,forwardInvocation:一般被用于将消息转发到其余对象。
为了了解转发的能力范围和目的,想一想一下场景:假设,首先,咱们设计一个对象可以响应一个叫作negotiate的消息,而且咱们咱们但愿这个消息的响应中包含另一种对象的响应。经过在negotiate的实现体中将negotiate消息传递给另外的对象,咱们可以轻松的完成这个任务。
进一步想一想,并假设咱们想要咱们的object为一个negotiate消息作出的响应,是另一个类的实现体。一种实现方式是让咱们的类继承另外的类。然而,咱们不必这么作,由于当前的类和实现了negotiate的类是在不一样的继承层次分支上。
即便咱们的类不经过继承也能得到negotiate方法,咱们可以“借”到这个方法,简单的版本就是经过将消息传递给其余类的实例:
- (id)negotiate { if ( [someOtherObject respondsTo:@selector(negotiate)] ) return [someOtherObject negotiate]; return self; }
这种方式显得比较笨重,尤为是,若是咱们有大量的消息须要object传递给其余的对象。咱们不得不以这种方式重写每一个咱们要“借”的方法。此外,他将不可能处理咱们漏掉或者不知道的方法;即使咱们将包含object全部消息的集合都以这种形式复写了,但是这些消息都是依赖于运行时的,而且在将来某个时刻他们可能变成了由新的method和类响应。
由forwardInvocation:消息提供的第二次机会,针对此问题,提供更少许代码的解决方案,而且是动态的而不是静态的。它将像这样:当一个对象因为它没有匹配selector的method而不能响应某个消息时,系统将经过发送forwardInvocation:消息告知对象。每个对象都从NSObject继承了一个forwardInvocation:方法。只不过,NSObject版本的此方法只是简单的调用了doesNotRecognizeSelector:。经过重写NSObject版本的此方法并本身给出实现,咱们可以利用这个机会,经过forwardInvocation:将消息转发到其余对象。
为了转发消息,forwardInvocation:方法内应该这么作:
可使用invokeWithTarget:将消息发送出去:
- (void)forwardInvocation:(NSInvocation *)anInvocation { if ([someOtherObject respondsToSelector: [anInvocation selector]]) [anInvocation invokeWithTarget:someOtherObject]; else [super forwardInvocation:anInvocation]; }
被转发的消息的返回值会被返回到原来的发送者。全部类型的返回值可以被传递到发送者,包括ids,结构体,和双精度floating数字。
一个forwardInvocation:方法可以用做于未识别的消息的分配中心,将他们发给不一样的receiver;或者做为一个转发站,将消息发送到相同的目的地。它也能够将一个消息转义为别的消息;或者简单的“吃掉”某些消息,使得既没有相应也没有错误。一个forwardInvocation:方法也能讲几个消息结合起来,得到一个响应。forwardInvocation:可以作什么取决于实现这。它为转发链条的对象开启了加入编程设计的机会。
注意:仅当它们没法调用receiver内存在的method时,forwardInvocation:方法才会处理消息。例如,咱们想要咱们的object转发negotiate消息到另外的object,首先咱们的object不能有negotiate方法,若是有,消息将不会到达forwardInvocation:。
更多信息关于转发和调用能够参见NSInvocation类的说明。
转发能够模拟继承,能够用于提供多继承的效果。像图5-1中,一个经过转发响应图中消息的object,看起来像是借或者“继承”了另一个类的方法实现。
在这个插图中,一个Warrior类的实例将negotiate消息转发给一个Diplomat类的实例。Warrior将会看起来像Diplomat的negotiate实现,Warrior看起来好像响应了negotiate消息(尽管其实是Diplomat响应的)。
转发了消息的对象也所以“继承”了来自多重继承的两个分支——它本身的分支和实际响应消息的object。在上面的例子中,Warrior看起来好像它同时继承了Diploma和它本身的superclass。
转发提供许多特性,典型的就是多继承:可是,多继承和转发是两个不一样的事物:多继承将多个类的功能集结在一个对象上,它变得更大,多个对象的结合体;而转发,换句话说,是将某些响应与不相干的对象关联起来。它将问题分解成更小的目标,并将这些小目标与消息发送者关联起来。
Forwarding不只能够模拟多继承;经过forwarding,咱们能够用轻量级对象做为实质对象的“封面”或表明。代替品代替其余对象并接收发送到它的消息。
在Objective-C Programming Language中被称为“远程消息”的proxy就是一个代替品。一个proxy涉及到对的管理细节有:forwarding消息到远程receiver,在链接过程时确保参数值是被拷贝的和从新获取的,等等。可是它也不会尝试去作更多别的;它不复制远程object的函数功能只是给远程object一个本地地址,也就是它能在别的APP中接受消息的地址。
还有些其余类型的代替品objects。例如,假设,咱们有一个object,它是用来处理大量数据的——可能它建立了复杂的图片或者从硬盘中的一个文件里读取内容。配置这个object多是很费事间的,所以更倾向于懒加载——当真正须要它时或者系统资源是暂时闲置时。同时,为了APP中其余objects正常运行,咱们须要为这个object提供至少一个占位object。
在这种状况下,咱们在最初能够建立不彻底健全的object,代替的为它建立一个轻量级的对象。这个对象可以独自处理一些事情,例如请求数据,可是大多数状况下它只是做为一个为大object的占位,当消息来了,就将消息转发给它。当代替品的forwardInvocation:方法第一次接收到发往其余object的消息时,代替品将会先确认那个object是否存在,若是不存在就建立它。全部发往重量级object的消息都通过代理,所以剩下的程序中,代替品和重量级object在使用上是同样的。
尽管forwarding能够模拟继承,NSObject类毫不会混淆这二者。像respondsToSelector:和IsKindOfClass:能够经过继承层次使用,却不能经过forwarding链使用。例如,若是Warrior object被查询是否响应negotiate消息,
if ( [aWarrior respondsToSelector:@selector(negotiate)] ) ...
这个返回值将会是NO,即便它可以接收negotiate消息并不报错的响应该消息,例如经过forwarding 消息到Diplomat。( 见图5-1 )
大多数状况下,正确的答案是NO。可是也可能不是,若是咱们咱们设置一个代替品object来扩展一个类的能力,forwarding机制会像继承同样。若是咱们想要想要咱们的objects像真的继承了(它们转发消息的目标)对象;咱们须要从新实现respondsToSelector:和isKindOfClass:方法来包含咱们的forwarding规则。
- (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:方法也应该反应出forwarding规则。若是协议是被使用的,conformsToProtocol:方法应该一样的被添加到重写的列表中。类似的,若是一个object forwarding任何它接收到的远程消息,他应该有一个methodSignatureForSelector: ,这可以精确的返回最终响应forwarded消息的描述;例如,若是一个object是有能力将消息forward到他的代替品,咱们应该实现methodSingnatureForSelector:就像如下:
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { NSMethodSignature* signature = [super methodSignatureForSelector:selector]; if (!signature) { signature = [surrogate methodSignatureForSelector:selector]; } return signature; }
咱们应该尽可能吧forwarding规则的代码一块儿放在某处,包括forwardInvocation:。
这个高级技巧,仅适合真的没有别的解决方案时使用。它不是继承的替代品。若是咱们不得不使用这种技巧,就确保彻底掌握这些运转规律(作转发的类和被转发的类关于forwarding的机制)。
在本节中提到的method能够查看详细说明(NSObject )。更多关于invokeWithTarget:,参见NSInvocation类的说明。
为了协助runtime系统,编译器将每一个method的返回值类型和参数类型编码成一个特征string,并把这个string与method selector结合起来。这个编码方案在其余环境也是可用的,而且是公开用于 @encode() 编码指令。当给定一个type 说名,@encode() 返回一个string 编码该type;type可使int、一个指针(pointer)、一个带有标签的structure或者union、或者一个Class名称——任何类型,事实上,就是全部可用于做为C的sizeof()操做的类型。
char *buf1 = @encode(int **); char *buf2 = @encode(struct key); char *buf3 = @encode(Rectangle);
下面的表列出了类型编码。注意,当为归档和解档编码对象时,如下大部分是和编码一致的。不过,有些在写编码器时不能使用的编码也被列出来了;而且,也有一些编码不是经过 @encode 生成的。(关于更多归档和解档编码objects,参见NSCoder 类说明。)
表6-1 OC type encodings
编码 | 类型 |
---|---|
c | A char |
i | An int |
s | A short |
l | A long 。 l is treated as a 32-bit quantity on 64-bit programs |
q | A long long |
C | An unsigned char |
I | An unsigned int |
S | An unsigned short |
L | An unsigned long |
Q | An unsigned long long |
f | A float |
d | A double |
B | A C++ bool or a C99 _Bool |
v | A 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 |
b (num) | A bit field of num bits |
^ | A pointer to type |
? | An unknown type (among other things, this code is used for function pointers) |
OC不支持long double类型。@encode(long double) 返回 d,就是说和double的编码一致。
Array的编码是闭合的中括号;array中的元素在开括号紧接着后面。例如,包含12个指向float的指针编码以下:
[12^f]
Structures是使用大括号,unions使用小括号;structure的标记(struct)是被列出来的首先,而后是大括号,以及被列在其中的元素。例如:
typedef struct example { id anObject; char *aString; int anInt; } Example;
将会被编码为:
{example=@*i}
不管是 @encode() 传入的是type 名称(Example)或结构体标记(example),都会获得相同的编码。为结构体指针的编码以下:
^{example=@*i}
不过,更高阶的结构体指针,将不会显示结构体内在的类型:
^^{example}
Objects 是被视为结构体,例如将NSObject类名传入 @encode():
{NSObject=#}
NSObject类只声明了一个示例变量,isa,也就是Class类型的
注意,尽管存在 @encode() 指令不返回值,runtime系统会使用额外的编码列表,如表6-2,当类型限定符被用于声明protocol中的methods时。
表6-2 OC method encodings
编码 | 类型 |
---|---|
r | const |
n | in |
N | inout |
o | out |
O | bycopy |
R | byref |
V | oneway |
当编译器碰见属性声明(参见Objective-C Programming Language 中的声明的属性),它生成与闭合的类、分类、Protocol相关联的描述性的元数据。咱们可以获取这些元数据经过使用函数,这些函数支持借助类或Protocol的name查找属性,获取属性的type(就像 @encode 得到的string),拷贝一个由C string构成的数组的property 的attributes。声明的属性列表对每一个Class和Protocol都是可获取的。
Property结构体为property 描述符号定义一个不透明的操做。
typedef struct objc_property *Property;
咱们可以使用class_copyPropertyList和protocol_copyPropertyList来分别获取与Class(包括被夹在的分类)以及Protocol相关联的属性数组。
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
例如,给定下面的类的声明:
@interface Lender : NSObject { float alone; } @property float alone; @end
咱们可以经过下面的方式获取属性的列表:
id LenderClass = objc_getClass("Lender"); unsigned int outCount; objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
咱们使用property_getName函数查找属性的name:
const char *property_getName(objc_property_t property)
咱们可使用class_getProperty和protocol_getProperty,借助一个类中指定的name获取类或Protocol中的一个property的引用。
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_getAtributes函数获取一个property的name和 @encode 类型string。更详细的编码类型string,参见类型编码;此string更详细参见Property Type String和Property Attribute Description。
const char *property_getAttributes(objc_property_t property)
把这些结合在一块儿,咱们可以将与类相关的说有属性列表打印出来:
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)); }
咱们可使用property_getAttributes函数获取到property的name和 @encode 类型的字符串,以及property的其余特征。
String以T开头,以后跟随着 @encode type 和一个逗号;最后面跟随一个V,以后是实例变量的name。在这两块之间,会插入下面的描述符,经过逗号间隔:
Table7-1声明property type 编码
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
|
The property defines a custom getter selector name. The name follows the G (for example,GcustomGetter,). |
S
|
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
|
Specifies the type using old-style encoding. |
更多实例参见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声明和property_getAttributes返回的string:
Property 声明 | 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 |
注意,关于(非id)Class类property_getAtributes以下:
@property (nonatomic,strong)NSString * maStingClass; fprintf(stdout, "%s", property_getAttributes(property)); 得到的字符串为: T@"NSString",&,N,V_maStingClass