06@理解“属性”这一概念

408 阅读3分钟
  • 可以用@property语法来定义对象中所封装的数据
  • 通过“特质”来指定存储数据所需的正确语义
  • 在设置属性所对应的实例变量时,一定要遵从改属性所声明的语义。
  • 开发iOS程序是应该使用nonatomic属性,因为atomic属性会严重影响性能。

成员变量

@interface EOCPersion: NSObject {
@public 
    NSString *_firstName;
    NSString *_lastName;
@private
    NSString *_someInternalData;
}

定义实例变量的作用域。OC中很少这样做。这写法的问题:

对象布局在编译器就固定,碰到实例变量,把实例变量替换为“偏移量”。偏移量为该变量距离存放对象的内存区域的起始地址有多远。
如果重新增加实例变量,会改变内存布局,需要重新编译。 OC解决此问题的方法是:把实例变量单做一种存储偏移量所用的“特殊变量”,交由类对象保管。

属性

属性 = 成员变量 + set方法 + get方法

@interface EOCPerson: NSObject
@property NSString *firstName;
@end

编译器会为属性自动添加成员变量和set方法与get方法。 如果不需要编译器,添加@dynamic

@interface EOCPerson: NSObject
@dynamic firstName, lastName;
@end

属性特质

原子性

编译器默认所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备nonatomic特质,则不使用同步锁。

@property (nonatomic, copy) NSString *firstName;

读写权限

  • readwrite(读写权限),同时获得setter和getter方法。默认
  • readonly(只读),只有get方法。
//.h文件
@interface ViewController : UIViewController

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

@end

//.m文件
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _firstString = @"内部任意修改";
}

/// 函数返回值置为NO,即不允许直接访问实例变量。禁止KVC来修改
+(BOOL)accessInstanceVariablesDirectly {
    return NO;
}
@end

内存管理语义

  • assign: 纯量类型(CGFloat、NSInterger等)的简单赋值操作

atomic与nonatomic区别:具备atomic特质的获取方法会通过锁定机制来确保其操作的原子性。如果两个线程读写同一属性,不论何时总能看到有效的属性值。如果不加锁,一个线程更改属性,一个读,会出现线程读到的属性值不对。iOS开发中使用同步锁开销较大,带来性能问题,所以一般不要求属性必须是原子的。不是绝对安全,就算是atomic,也可能会读到不同的属性值。

  • strong: 拥有关系。先保留新值,并释放旧值,然后将新值设置上去
  • weak:非拥有关系,既不保留新值,也不释放旧值。同assign,属性所指对象遭到摧毁是,属性值会清空
  • unsafe_unretained: 同assign,适用对象类型,非拥有关系。属性所指对象遭到摧毁是,属性值不会自动清空
  • copy: 拥有关系。设置方法并不保留新值,而是将其拷贝。常用来保护NSString的封装性,因为传递给设置方法的新值可能指向一个NSMutableString类的实例。

方法名

  • getter=:指定获取方法的方法名。@property (nonatomic, getter=isOn) BOOL on;
  • setter=:指定设置方法的方法名。