Objective-C属性详解

2015-09-23  本文已影响279人  fever105

Objective-C属性详解

理解属性

谈到属性,人们总是说这个对象的属性指向一个什么对象,把那个属性设置为一个值,等等。这样的说法,让人觉得属性是一个具体的事物,是对象的一部分。

但是,个人认为,属性其实是一种基于面向对象编程的机制。通过这个机制,我们得以将数据封装在对象内,这里的封装包括存入和获取数据的动作,以及存放的地点

没错,上面提到的动作和地点分别暗指存取方法实例变量,它们支持着属性机制。两者缺一不可,没有它们,属性机制也就不能正常运行。

@property指令

关于@property指令的具体作用,苹果文档也没有一个明确统一的说法。但是经过试验和总结,它有如下主要功能。

@property NSString *word;为例:

  1. 定义了名为_word的的实例变量。
  2. 声明和实现了名为- (NSString *)word- (void)setWord:的存取方法。

也就是说,利用@property声明属性,就构建由实例变量和存取方法支持的属性机制。

实例变量

作为保存数据的地点,实例变量对于对象来说是不可或缺的。除了使用@property指令自动对其进行定义之外,还可以手动定义实例变量,例如:

@interface Test : NSObject {
    NSString *_word; // 定义一个实例变量_word
}

@end

这样定义出来的实例变量同使用@prperty定义的效果完全相同。

存取方法

有了保存数据的地点,存放和获取数据的手段也必不可少。除了使用@property自动实现默认的存取方法以外,也可以手动声明和实现存取方法。例如:

@interface Test : NSObject {
    NSString *_word; // 定义一个实例变量_word
}
- (void)setWord:(NSString *)w;
- (NSString *)word;
@end

// 以上是interface,以下是implementation 

@implementation Test

// 实现_word的默认存取方法
- (void)setWord:(NSString *)w
{
    _word = w;
}

- (NSString *)word
{
    return _word;
}
@end


事实上,通过@property所实现的默认存取方法同上面的实现一致。(除了在对属性使用atomic限定词的情况下,稍后会解释)

架空属性

上面提到过,属性机制由实例变量和存取方法支持,二者缺一不可。但是,它的灵活之处在于,属性的实例变量和存取方法之间也存在依赖关系:存取方法之中至少有一个必须通过@property自动实现,否则,实例变量将不会被自动定义。也就是说,如果同时手动实现了存取方法(在只读情况下实现了取方法),那么就没有实例变量可用了。例如:

@interface Test : NSObject 

@property NSString *word;

@end

// 以上是interface,以下是implementation 

@implementation Test

// 实现_word的默认存取方法
- (void)setWord:(NSString *)w
{
    _word = w; // 报错
}

- (NSString *)word
{
    return _word; // 报错
}
@end

上面的代码,word的存取方法中引用_word的地方会报错,提示实例变量_word不存在。这时,编译器会认为你想架空属性机制,所以不再自动定义实例变量。

之所以有这样的设定,是因为通常情况下,默认的属性机制可以满足我们的正常使用;但是,在某些情况下例外,例如一个属性完全依赖于另一个属性,以至于它们存放数据的地点都是同一个,所以,它们共用一个实例变量即可。

@synthesize指令

在一些较早的书籍中,经常出现在.h文件中使用@property定义属性,然后在.m文件中使用@synthesize合成属性的情况出现,书上解释为:合成相应属性的实例变量和存取方法。

但是,截至本文写作之时,利用@property声明属性之后,不论是否使用@synthesize都会自动定义实例变量以及实现存取方法。这样看来,@synthesize的功能稍显多余。

但是,除了这些重复功能之外,@synthesize还有一些用法,值得注意。

  1. 手动定义实例变量。在同时实现存取方法的情况下,默认实例变量不在自动定义。这时,使用@synthesize可以定义一个可用的实例变量。
  2. 修改实例变量的名称。默认情况下,实例变量的名称是_属性名。通过@synthesize 属性名 = 新实例变量名的形式,可以修改其名称。

注意@synthesize必须和@property配套使用。例如,如果没有使用@property,而是手动定义了实例变量,再用@synthesize实现存取方法,是不可以的。此时,只能手动实现存取方法。

属性特性

属性可以拥有一组特性,用来限定存取方法的行为,写在小括号里。例如:

@property (nonatomic, readonly, strong) NSString *word;

有三类:

  1. 多线程特性:atomic / nonatomic。前者可以保证属性在通过多线程存取是不会发生错误。但注意,在使用atomic的情况下,不能再手动生成存取方法,因为它们的实现包含了线程管理的内容。一般都用nonatomic。

  2. 读写特性:readonly / readwrite。前者只允许实现取方法。

  3. 内存管理特性:又分为非ARC模式和ARC模式下。

    • 非ARC
      • retain:限定指针类型的变量,指针所指向的对象的引用计数加1。
      • assign:限定非指针和id类型的变量,不对引用计数进行操作。
      • copy:限定指针类型的变量,复制接收copy消息的对象,并将变量指向这个新的对象。多用于防止可变类型的对象放生改变。
    • ARC
      • strong:作用同非ARC模式下的retain。
      • weak:限定指针类型的变量,作用同非ARC模式下的assign,但是,但变量所指向的对象被释放后,指针自动被设置为nil
      • assign:限定非指针类型的变量,如int,float,结构体等基本数据类型。也可以用关键字unsafe_unretained来表示。
上一篇下一篇

猜你喜欢

热点阅读