Objective-C Properties
来源:互联网 发布:java开发网上商城 编辑:程序博客网 时间:2024/06/13 11:47
Properties
一个对象的属性可以使其他对象检查或改变其状态. 但是, 对于一个好的面向对象程序设计而言, 外部不可能直接访问对象内部的状态. 相反, 存取方法(accessor methods(getters & setters))被用作与对象底层数据交互的抽象概念.
@property
指令的目的是使通过自动生成这些存取器方法来创建及配置属性变得容易. 它允许你在语义层面指定公共属性的行为, 同时顾及了实现部分的细节.
The @property Directive
首先来看看在使用@property
时候究竟发生了什么. 假设以下是一个简单Car
类的接口和实现部分.
// Car.h#import <Foundation/Foundation.h>@interface Car : NSObject@property BOOL running;@end
// Car.m#import "Car.h"@implementation Car@synthesize running = _running; @end
编译器生成了为属性running
生成了一个访问器和设置器, 默认的命名习惯是使用属性本身作为访问器, 加前缀set
作为设置器, 在属性前加下划线作为实力变量:
- (BOOL)running{ return _running;}- (void)setRunning:(BOOL)newValue { _running = newValue;}
在使用@property
声明属性之后, 就可以像在类的接口和实现文件中调用这些方法一样调用它们. 同时也可以在Car.m
中重写它们以支持自定义的getter/setters, 但是这样做会使@synthesize
成为强制性的. 然而, 很少的自定义存取器, 因为@property
属性语义在抽象层面让你这样做.
通过点语法访问属性时, 在幕后会转换到上述存取器方法, 因此, 当分配值给属性以及在running
方法读取值的时候, 下面honda.running
代码真正意义上是调用了setRunning:
方法.
// main.m#import <Foundation/Foundation.h>#import "Car.h"int main(int argc, const char * argv[]) { @autoreleasepool { Car *honda = [[Car alloc] init]; honda.running = YES; // [honda setRunning:YES] NSLog(@"%d", honda.running); // [honda running] } return 0;}
为了改变生成的访问器的行为, 你可以在@property
之后括号内指定属性语义.
The getter= and setter= Attributes
如果你不喜欢@property
默认的命名习惯, 可以使用getter= 和 setter=
语义更改getter/setter方法名. 常用的是为Boolean属性更改名字, 按常规其getter前加前缀is
.
@property (getter=isRunning) BOOL running;
现在, 合成的访问器称作isRunning
和setRunning
. 注意, 公共属性仍然为running
, 而且必须使用点语法:
Car *honda = [[Car alloc] init]honda.running = YES; // [honda setRunning:YES]NSLog(@"%d", honda.running); // [honda isRunning]NSLog(@"%d", [honda running]); // Error: method no longer exists
The readonly Attribute
readonly
是仅使属性具有只读的语义. 它删掉了setter方法阻止通过点语法给属性赋值, 但是getter方法未受影响. 如下, 改变Car
的接口部分. 注意如何通过逗号(,)分隔来说明多重语义.
#import <Foundation/Foundation.h>@interface Car : NSObject@property (getter=isRunning, readonly) BOOL running;- (void)startEngine;- (void)stopEngine;@end
为了不让其他对象改变running
属性, 我们在内部通过startEngine
和stopEngine
方法来设置. 对应的实现如下:
// Car.m#import "Car.h"@implementation Car- (void)startEngine { _running = YES;}- (void)stopEngine { _running = NO;}@end
@property
同时为我们合成了一个实例变量, 这就是为什么我们可以访问_running
而不用在任何一处声明它(此处不能使用self.running
, 由于属性是只读的). 在main.m
函数中添加以下代码来测试这个新的Car
类:
Car *honda = [[Car alloc] init];[honda startEngine];NSLog(@"Running: %d", honda.running);honda.running = NO; // Error: read-only property
直到此时, 属性做到了真正的便捷简写, 避免书写getter和setter方法的样板代码. 这不是其他剩余语义的例子, 它显著地改变了属性的行为. 它们仅适用于存储Objective-C对象属性(相对于C原始的数据类型).
The nonatomic Attribute
原子性控制属性在线程环境下的行为. 如果你有多条线程时, setter和getter有可能会被同时调用. 这意味着getter/setter会被另外一个操作给打断, 很有可能产生乱码.
原子性属性锁定了底层对象以阻止这种可能性发生, 保证get或set操作具有一个完整地值. 但是, 理解这个只是线程安全使用的一方面是重要的, 原子性不在需要意思是你的代码是线程安全的.
@property
声明的属性默认的是原子性的, 这导致一些. 如果你没有在多线程环境, 那么使用nonatomic
语义来重写这种行为.
@property (nonatomic) NSString *model;
原子性属性同样也有小而实用的警告. 原子性属性访问器必须同时具备合成或用户自定义性.
Memory Management
在任何一个面向对象编程(OOP)语言中, 对象都在计算机内存当中-特别是对于手机设备来说, 内存是相当稀缺的资源. 内存管理系统就是为了确保程序不会占有多余的空间, 它们必须以高效的方式来创建和销毁对象.
大多语言通过垃圾回收(garbage collection)机制来完成该过程, 但是Objective-C使用了更有效地替代方案, 称之为object ownership. 当你和一个对象进行交互的时候, 你拥有了那个对象, 这意味着保证在你对象交互的期间它是一直存在的. 当你与其交互完毕, 你放弃持有该对象并且-假如该对象没有其他的持有者-操作系统销毁对象并释放底层内存.
随着自动引用计数的到来, 编译器自动地管理了所有对象持有关系. 大部分来说, 这意味着你再也不必担心内存管理系统究竟是如何运行的, 但是你必须理解@property
的strong
, weak
和copy
语义, 因为他们告诉编译器对象所被持有的关系.
The strong Attribute
strong
语义创建了一种拥有关系, 对于不论哪个对象被分配给其属性. 这是一种对所有对象来说的隐式行为, 这是一种默认的安全行为, 由于它保证其值和属性的生命周期一样.
让我们通过创建另外一个类Person
看看是如何工作的. 该类接口仅声明了name
:
// Person.h#import <Foundation/Foundation.h>@interface Person : NSObject@property (nonatomic) NSString *name;@end;
实现部分如下, 使用了由@property
默认生成的访问器. 同时重写了NSObject的description方法, 返回字符串代替对象.
// Person.h#import "Person.h"@implementation Person- (NSString *)description { return self.name;}@end
接下来, 给Car
类添加一个Person
属性. 如下,
// Car.h#import <Foundation/Foundation.h>#import "Person.h"@interface Car : NSObject@property (nonatomic) NSString *model;@property (nonatomic, strong) Person *driver;@end
main.m
函数中:
// main.m#import <Foundation/Foundation.h>#import "Car.h"#import "Person.h"int main(int argc, const char * argv[]) { @autoreleasepool { Person *john = [[Person alloc] init]; john.name = @"John"; Car *honda = [[Car alloc] init]; honda.model = @"Honda Civic"; honda.driver = john; NSLog(@"%@ is driving the %@", honda.driver, honda.model); } return 0;}
由于driver
是strong语义, 对象honda
拥有john
的所有权, 这样保证了在honda
需要时它是可用的.
The weak Attribute
大多时候, strong
语义是凭直觉来确认你想要的对象属性. 但是, 强引用也有问题, 比如如果我们需要一个从driver
到Car
对象的引用, 首先为Person.h
添加一个car
属性:
// Person.h#import <Foundation/Foundation.h>@class Car;@interface Person : NSObject@property (nonatomic) NSString *name;@property (nonatomic, strong) Car *car;@end
@class Car
一行是Car
类的向前声明(forward declaration). 它告诉编译器, “相信我, Car
类是存在的, 因此没必要此刻就去找到它.” 我们必须用此来代替通常使用的#import
因为在Person.m
中也要引入Car
, 不然就会产生循环引入.
接下来, 在main.m
中, honda.driver
后添加如下语句:
honda.driver = john;john.car = honda; // add this line
现在, honda
对john
具有持有关系, 而且john
对honda
也有同样关系. 这意味着两个对象之间永远拥有对方, 那么内存管理系统将在其不使用的情况下也无法进行销毁.
这种现象被称为循环引用(retain cycle), 这是内存泄露的一种形式. 所幸的是, 修复这种问题非常简单, 只需要将一个对象对另一个对象保持若引用(weak reference)即可. Person.h
中, 改变car
的声明如下:
@property (nonatomic, weak) Car *car;
weak
语义对car
创建的以非拥有的关系. 这允许john
对honda
有引用而避免了循环引用. 但同时, 这也意味着有可能honda
在john
对其还有引用存在时被销毁. 如果发生这种情况, weak
语义会很方便的将car
置为nil
而避免野指针.
weak
语义常用的用法是用于父-子数据结构. 习惯上, 父对象应该对子对象持有强引用, 而子对象则应该对父对象持有弱引用. 弱引用也是代理设计模式(delegate design pattern)固有的部分.
解决的办法就是两个对象之间不要全是强引用. weak
语义使得保持有周期性的关系(cyclical relationship)同时避免了循环引用成为了可能.
The copy Attribute
copy
语义是strong
的替代品. 与获取存在对象的持有权不同, copy
创建了一个属性的副本, 然后对副本持有. 仅有遵从NSCopying protocol的对象才能使用该语义.
属性代表值(相对连接或关系)非常易于复制. 比如, 开发者通常复制NSString
属性而非强引用:
// Car.h@property (nonatomic, copy) NSString *model;
此时, Car
会存储一个我们分配给model
的新的实例. 如果你使用了可变的值, 这会有额外的好处, 可以在当对象分配了可变值的时候被, 不论再次分配任何值都不会变.
// main.m#import <Foundation/Foundation.h>#import "Car.h"int main(int argc, const char * argv[]) { @autoreleasepool { Car *honda = [[Car alloc] init]; NSMutableString *model = [NSMutableString stringWithString:@"Honda Civic"]; honda.model = model; NSLog(@"%@", honda.model); [model setString:@"Nissa Versa"]; NSLog(@"%@", honda.model); // Still "Honda Civic" } return 0;}
NSMutableString
是NSString
的子类, 它能够就地被编辑. 如果model
属性没有创建初始实例的副本, 我们会在第二个输出函数NSLog()
中看到改变之后的字符串(Nissan Versa
).
Other Attributes
以上@property
语义是新版本Objective-C应用(iOS 5+)所必须的, 但是有一些其他的语义你可能会在早起库或文件中发现.
The retain Attribute
retain
语义是手动内存管理(Manual Retain Release)版本中的strong
, 并具有相同的影响: 声明了分配值的所有权. 在自动引用计数中不会用到它.
The unsafe_unretained Attribute
具有unsafe_unretained
语义的属性行为类似于weak
语义的属性, 但是如果饮用对象被销毁它们不会自动置为nil
. 你必须使用unsafe_unretained
的原因是使你的类和代码兼容, 二代码不支持weak
属性.
The assign Attribute
当分配给属性新值时, assign
语义不会执行任何一种内存管理调用. 这是早期数据类型的默认行为, 在iOS 5 之前它是用于实现弱引用的. 比如retain
, 不必在现代应用中明确使用它.
Summary
本节展现了@property
全部可用的语义, 我们希望你对修改合成访问器方法行为感到相对舒适. 所有这些语义的目标是帮助你聚焦什么样的数据需要记录, 允许编译器自动地决定如何展示.
getter=
为getter方法使用自定义的名字 setter=
为setter方法使用自定义的名字 readonly
不用合成setter方法 nonatomic
不保证在多线程环境下访问器的完整性. 这比默认的原子性 strong
在属性和被分配的值之间创建一个拥有关系. 这是对象属性默认的 weak
在属性和被分配的值之间创建一个非拥有关系. 使用该语义阻止循环引用 copy
创建一个被分配值的副本来代替引用本文翻译自:http://rypress.com/tutorials/objective-c/properties
- Objective-C Properties
- Using Properties in Objective-C Tutorial 边看边记
- Adding Properties to an Objective-C Category
- Properties vs. instance variables(ios objective-c)
- Ry’s Objective-C Tutorial---Properties
- Objective-C Primer(2)Private Methods and Class Properties
- Adding Properties to an Objective-C Category – Revisted
- Effective Objective-C 2.0: Item 6: Understand Properties
- Effective Objective-C 2.0:Item 26: Avoid Properties in Categories
- Objective-C 运行时编程指南 之 Declared Properties
- Properties(C#)
- Objective-C
- Objective-C
- Objective-C
- Objective-c
- Objective-c
- Objective-C
- Objective-C
- ROS总结——ROS节点
- 2的幂次方表示
- Fighting_小银考呀考不过四级
- Android NotificationsetLatestEventInfo弃用和NotificationBuilder用法(1/2)
- 试论推行硬件操作系统(HOS)的可能性及必要性
- Objective-C Properties
- 浅谈网络流的基本算法
- Java——万事万物皆对象
- pycharm 2016.3注册码
- 经典算法——插入排序法
- localstorage、sessionstorage和cookie的区别(面试常问)
- shell脚本直接执行没有问题,crontab定时执行失败的解决方法
- 怎么关闭谷歌浏览器自动断点调试?打开网页按F12之后,打开任何网页都会自动断点调试js
- [编程题] 优雅的点