Objective-C 编程语言(5)属性----属性的声明与实现

转载请标明出处: http://blog.csdn.net/zhangxingping

属性的声明与实现

    属性的使用包含两点:属性的声明和实现。

 

属性的声明

   属性的声明以关键字@property开始。该关键字可以出现在类的接口定义@interface中的方法列表中的任何地方。同时,@property还可以在协议或者是类别的声明中出现。

   @property(attributes) type name;

   其中的@property指令用来声明属性。其后可选的括号中的项是对属性的存储方式和其行为的细节描述。和其他的Objective-C中的类型一样,每一个属性都有一个类型修饰符和名称。

   列表5-1展示了简单的属性的声明

@interface MyClass:NSObject
{
    float value;
}
@proerty float value;
@end 

列表5-1

   我们可以把属性的声明看作等同于声明了两个访问方法。因此

   @property float value;

   相当于:

   -(float) value;

   -(void) setValue:(float)newValue;

   然而,属性的声明为如何实现对应的访问方法提供了更多的信息。(具体请参见“声明属性的属性”)。

  

属性的属性(Property Declaration Attributes)

   译者注:Property和Attribute直译过来都是“属性”,为了避免混淆,本小节中分别使用Property和Attribute两个英文单词。

   我们可以使用下面的形式来对Property进行修饰:

   @property( attribute [, attribute2, ...])

   和方法一样,Property的作用域局限于声明他的接口中。如果在声明Property时使用了逗号把多个Property的名称分隔开,那么其中的Attribute对所有Property都是适用的。

   如果我们使用了@synthesize指令来告诉编译器自动创建访问方法,那么编译器创建的访问方法将是符合Attribute中的描述的。如果我们自己实现这些访问方法,我们必须保证自己的实现和Attribute中的描述是相一致的。(例如,如果我们在Attribute中指定了copy关键字,那么我们必须在实现setter的时候对输入的值进行copy赋值)。

 

访问方法的名称

   和属性相关的getter和setter方法的缺省名称为:属性名称和set属性名称。例如,有属性,其名称为foo,那么缺省的getter方法的名称就是foo;setter方法的名称就为setFoo:。下面的attribute允许我们来自己定义getter和setter方法的名称。这些attribute都是可选的,并且几乎可以和其他的attribute一起出现(例外:readonly和setter=不能同时出现)。

   getter=getterName

       用于指定属性getter方法的名称。属性的getter方法的返回值必须和属性的类型是一样的,并且不能接收参数。

   setter=setterName

       由于指定属性的setter方法的名称。书香的setter方法必须接收一个和属性类型相同的参数,并且返回值必须是void的。如果属性被指定为是readonly(只读)的,那么指定其setter方法将会导致编译器报告错误。

   通常,我们指定的方法方法的名称都要符合key-value编码风格。使用getter修饰符的一个原因就是为了和...

 

可写性

   下面的attributes将限定property是否有相关的setter方法。他们是互斥的。

   readwrite

       表示属性是可读写的。这个也是缺省的attribute。

       在实现代码中,getter和setter都是要实现的。如果在实现代码中使用了@synthesize指令,那么编译器会自动生成getter和setter方法。

   readonly

       表示property是只读的。

       如果使用了readonly attribute,那么在@impementation代码块中只需要实现getter方法。如果使用了@synthesize指令,那么也只有getter方法会被自动生成。如果使用点号运算符企图对其进行赋值,编译器会报错。

 

关于setter

   下面的attributes表明了setter方法设置值时候的方法。他们是互斥的。

   assign

       表明只是简单的赋值。这个是缺省的。

       通常只用于标量类型。例如,NSInteger和CGRect,或者是位置类型的对象,如delegetes。

       retian和assign在支持垃圾回收环境中是等效的。

   retain

       表明在对象赋值的时候应该持有该对象。

       之前的对象会被发送release消息。

       在Mac OS X v10.6之前的版本中,这个attribute只是用于Objective-C的对象类型(也就是说不能向一个Core Foundation对象发送retian消息的)。

       在Mac OS X v10.6及其之后的版本中,可以使用__attribute__关键字来表明Core Fundation属性应该按照Objective-C的对象来进行内存管理:

       @property(retian) __attribute__((NSObject)) CFDictionaryRef myDictionary;

   copy

       表明应该使用对象的一个副本来进行赋值。

       之前的对象会被发送release消息。

       副本是通过向对象发送copy消息而得到的。这个attribute只适用于对象类型,并且要求该对象实现了NSCopying协议。更多信息请参见“拷贝”章节。

   这些不同限制的使用和是否使用垃圾回收机制有关:

   ●如果没有使用垃圾回收机制,对于对象属性来说我们必须显示地指明是assign,retian还是copy。否则编译时候会报告错误或者警告(这种限制促使我们谨慎考虑自己到底需要使用什么样的内存管理机制并且显示地指定它)。

     为了明确自己到底需要什么样子的内存管理方式,我们需要深入了解Cocoa的内存管理策略(参见《内存管理编程指南》一书)。

   ●如果使用了垃圾回收机制,使用缺省的attribute(也就是没有指定是assign,retian还是copy)不会导致编译告警。除非属性对应的类型遵守NSCopying协议。

     缺省的attribute通常就是我们所需要的;如果属性的类型是可以复制的,为了达到封装的目的,通常我们都是会使用拷贝的副本。

 

原子性

   我们可以使用下面的attribute来标明访问方法是非原子的:

   nonatomic

       该attribute标明访问方法是非原子的。缺省情况下访问方法是原子的。

   缺省情况访问方法是原子的可以保证在多线程的环境中对属性的访问具有更好的鲁棒性。也就是说getter方法返回的值或者是setter方法设置值都是充分完好的,而不管多个线程是如何并发执行的。更多详细信息请参阅“性能和线程”章节。

   如果我们使用了retain或者copy而没有使用nonatomic,那么在引用计数环境中,自动生成的getter方法中会先上锁,然后在持有原来的对象,再向对象发送autorelease方法,最后解锁并返回其值。这种实现类似如下:

   [_internal lock] //使用对象级的锁

   id result = [[value retian] autorelease];

   [_internal unlock];

   return result;

   如果使用了nonatomic,自动生成的方法方法仅仅是简单直接地返回其值。

 

Mark and Deprecation

    翻译中...

 

实现属性的指令

    在@implementation代码块中我们可以使用@synthesize和@dynamic指令来除法特定的编译动作。注意:这两条指令不是必需得有的,也就是说可以不实用两者中的任何一个。

重要:如果在@implementation代码块中既没有为属性指定@synthesize和@dynamic指令,那么就必须提供该属性的getter和setter的实现。否则,编译时会报告错误。
 
    @synthesize

该指令告诉编译器如果在@impementation中没有实现属性的getter和setter就自动生成之。

列表 5-2中的代码就演示了@synthesize的用法

@interface MyClass : NSObject
{ 
    NSString * value;
}
@end

@impementation MyClass
@synthesize value;
@end

       列表 5-2

我们还可以使用propery = ivar的格式来为属性指定实例变量,例如:

@synthesize firstName, lastName, age = yearOld;

上面的写法告诉编译器应该为firstName, lastName和age生成getter和setter方法,并且age属性代表的就是实例变量yearsOld。更多关于自动生成getter和setter方法的信息和attribte选项有关(请参见“属性的属性(Property Declaration Attributes)”)

在为属性指定实例变量的时候,只能指定该类自己的实例变量,而不能指定其超类的实例变量。

和运行时相关的访问方法的自动生成和上面所说的是有些差别的(参见"运行时的差别"小节):

  • 在旧的运行时系统中,为属性指定的实例变量必须是在@interface快中声明的。如果没有为属性指定实例变量,但是类的实例中有同名的实例变量并且其类型与属性是相同的,则该他就是属性对应的实例变量;否则编译时会报告错误。
  •  在新的运行时系统中,实例变量会根据需要进行自动与属性关联。如果类中存在同名的实例变量,则会与之关联。

    @dynamic

@dynamic指令告诉编译器我们或者直接提供属性的实现方法,或者是在运行时使用诸如动态加载代码或者动态方法解析的方式来提供属性的实现方法。这个指令告诉编译器在不能找到属性访问方法实现的使用不用产生告警。我们只应该在明确知道属性的访问方法在运行时是可用的时候使用这个指令。

列表5-3中的类继承了NSNanagedObject类,其中演示了@dynamic指令的使用方法

@interface MyClass : NSManagedObject
{
}
@property(nonatomic, retain) NSString * value;
@end

@implementation MyClass
@dynamic value;
@end

列表 5-3

其中的NSManagedObject是核心数据框架(Core data framework)中的类。该类有自己的机制用来定义属性及其与自身类的关系。在运行的时候,核心数据框架会为其属性生成对应的访问方法。我们只需要声明属性,而不用提供对应的访问方法,也不用编译器自动生成对应的访问方法。如果我们只是声明了属性,而没有为其提供访问方法,那么编译器会报告警告。@dynamic指令就是用来消除这种警告的。

 

发布了22 篇原创文章 · 获赞 6 · 访问量 35万+

猜你喜欢

转载自blog.csdn.net/zhangxingping/article/details/7430497