导言

property 是 OC 的一项特性,用于封装目标中的数据。

OC 一般把所需求的数据保存为各种实例变量,实例变量经过存取办法来拜访,即 setter 和 getter

@property 的优势

尽量多的运用特点(property)而不是实例变量(attribute)由于特点(property)比较实例变量有很多的好处:

  • 主动合成 getter 和 setter 办法。当声明一个特点(property)的时分编译器默许状况下会主动生成相关的 getter 和 setter 办法

  • 更好的声明一组办法。由于拜访办法的命名约好,能够很明晰的看出 getter 和 setter 的用途。

  • 特点(property)关键词能够传递出相关行为的额定信息。特点提供了一些可能会运用的特性来进行声明,包含 assign(vs copy),weak,strong,atomic(vs nonatomic),readwrite,readonly 等

特点生成

@property 的实质是 ivar(实例变量) + getter + setter。
默许状况下 @property 是用 @synthesize (合成)润饰的(默许完结: @synthesize property = _property),主动生成 ivar + setter + getter。
ivar 是实例变量 编译器主动生成 _特点名 的实例变量

例如:


@property (nonatomic, readwrite, copy) NSString *name;
@synthesize name = _name;

@synthesize 会主动生成 setter 和 getter 办法,可是也能够省掉 @synthesize 关键字,让编译器主动合成 getter 和 setter 办法。

单独重写 getter 和 setter 办法没有问题,可是假如一起重写 getter 和 setter 办法,体系部会主动生成 _property 变量,编译器会报错,所以假如需求一起重写 getter 和 setter 办法,要增加 @synthesize


// 重写 getter 和 setter 办法
- (NSString *)name {
//有必要运用_name来拜访特点值,运用self.name来拜访值时编译器会主动转为调用该函数,会形成无限递归
return _name;
}
- (void)setName:(NSString *)name {
    //有必要运用_name来赋值,运用self.name来设置值时编译器会主动转为调用该函数,会导致无限递归
    //运用_name则是直接拜访底层的存储特点,不会调用该办法来赋值
    //这儿运用copy是为了避免NSMutableString多态
    if (_name != name) {
    _name = [name copy];
    }
}

property 常用指示符

存取器办法 getter setter

指定获取特点目标的名字为 getterName,假如你没有运用 getter 指定 getterName ,体系默许直接运用 propertyName 拜访即可。一般来说,只要所指特点需求我们指定 isPropertyName 对应的 Bool 值时,才运用指定 getterName ,一般直接用 PropertyName 即可。
setter=setterName: 则是用来指定设置特点所运用的的 setter 办法,即设置特点值时运用 setterName: 办法,此处 setterName 是一个办法名,因而要以”:”结束,详细示例如下:


// 指定getter办法名为isBlue
@property (nonatomic, readonly, getter=isBlue) BOOL blue;
// 指定setter办法名为 theNickname
@property (nonatomic, copy, setter=theNickname:) NSString *nickname;

原子性 nonatomic atomic

atomic(默许特点):原子性拜访,编译器会经过默许机制(确认机制)保证 getter 和 setter 完整性(体系给主动生成的 getter 和 setter 办法进行加锁)。可是 atomic 并不是绝对线程安全,A 先成进行写操作后(写完结),B 线程又进行写,A 线程再读取就不一定是之前写入得值(可能是 B 的)。假如是 MRC,C 线程进行了 release 操作,会 crash,损坏了线程安全,所以要增加锁等操作来保证线程安全。所以 atomic 仅仅了保证了存取办法的线程安全,并不能保证整个目标是线程安全的(如 NAArray 的 objectAtIndex:就不是线程安全的,需求加锁等保证线程安全)。
nonatomic:非原子性拜访,不保证 setter 和 getter 的完整性,实质来说便是去掉了 atomic 对 getter 和 setter 办法增加的锁(体系不会给主动生成的 getter 和 setter 办法进行加锁),也便是 getter 和 setter 办法不是线程安全的,比如,当 A 线程进行写操作,B 线程突然把未修改好的特点值提取出来,时分线程读到的值不一定是对,iOS 中同步锁开销过大会带来性能问题,一般状况下不要求特点是 atomic,由于其自身不能保证线程安全。


@property (nonatomic, readwrite, copy) NSString *name;
@property (atomic, readwrite, copy) NSString *atomicName;

读写权限 readwrite readonly

readwrite(编译器默许特点):可读可选权限,若该特点由@synthesize 润饰,主动生成对应的 getter 和 setter 办法。
readonly:只读权限,若该特点由@synthesize 润饰,只生成 getter 办法,不生成 setter。

@property (nonatomic, readwrite, copy) NSString *name;
@property (nonatomic, readonly, getter=isBlue) BOOL blue;

内存管理:assign strong weak copy retain unsafe_unretained

assign 是指针赋值,没有引证计数操作,目标毁掉之后不会主动置为 nil
assign 对特点仅仅简略的赋值操作,不更改赋值新值的引证计数,即不进行 retain 操作,也不改动旧值的引证计数,常用于标量类型,NSInteget、NSUInteget、CGFloat、NSTimeInterval、Bool 等。
assign 也能够润饰目标如 NSString 等类型目标,由于不改动引证计数,所以当新值的引证计数为 0 目标被毁掉时,当前特点并不知道,编译器不会将该特点置为 nil,指针依然指向之前被毁掉的内存,这个时分拜访该特点会发生野指针,并 crash,所以 assign 润饰的类型一定是标量类型。比如


@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, assign) NSString *assignName;

copy copy 一个新目标,引证计数+1。
当调用润饰目标的 setter 办法时会树立一个新目标,引证计数+1,即目标会在内存里复制一个副本,两个指针指向不同的地址
copy 一般用来润饰有可变类型子类的目标:NSArray,NSDictionary,NSString,
为保证这些不可变目标由于可变子类目标影响,需求 copy 一份备份,假如不运用 copy 润饰,运用 strong 或 assign 等润饰则会由于多态导致特点值被修改。比如


@property (nonatomic, copy) NSArray *array;

block 也用 copy 润饰。


@property (nonatomic, copy) ABlock *block;

关于可变类型如 NSMutableString、NSMutableArray、NSDictionary 则不能用 copy 润饰,由于这些类都完结了 NSCopying 协议,运用 copy 办法回来的都是不可变目标。
即假如运用 copy 润饰符在对可变目标赋值时会获取一个不可变目标,接下来假如对这个目标进行可变目标的操作会发生反常 Crash,由于没有 mutableCopy 润饰符,关于可变目标运用 strong 润饰符来润饰。比如


@property (nonatomic, strong) NSMutableArray *mutableArray;

strong 强引证,引证计数+1

strong 表示特点对所赋值的目标强引证,是一种具有联系,增加新值的引证计数,开释旧值削减引证计数,
润饰目标的引证计数会+1,当目标的引证计数为 0 时,目标就会在内存中开释,一般润饰目标类型、可变调集、可变字符串


@property (nonatomic, strong) NSMutableString *strongName;

retain 强引证,引证计数+1
MRC 下的强引证润饰词,跟 strong 同理,ARC 中被 strong 替代

weak 弱引证,没有引证计数操作,目标毁掉之后主动置为 nil
weak 表示特点对所赋值的目标弱引证,是一种非具有联系,所赋的值在引证计数为 0 被毁掉后,weak 润饰的特点会被主动置为 nil 能够有用避免野指针错误。
weak 一般用来润饰代理 delegate,避免循环引证。weak 与 assign 不同,只能润饰目标类型


@property (nonatomnic, assign) id <AProtocol> delegate;

unsafe_unretained 弱引证,没有引证计数操作,润饰的目标毁掉之后指针不会主动置为 nil,假如此刻在调用该指针会发生野指针:EXC_BAD_ACCESS错误

不安全非具有,相似 assign 非常少用 但只能润饰目标类型,不能润饰标量类型

分类 Category 中增加特点

在分类中,增加@propety 特点只会生成 setter 和 getter 办法的声明,并不会有详细的完结,认为 Category 在运行时目标的内存布局就已经确认了,假如此刻增加实例变量就会损坏目标的内存布局,所以 Category 无法增加实例变量。
所以如安在 Category 完结实例变量功能呢?能够经过 Runtime 增加关联目标完结成员变量:

@property (nonatomic, assign) NSTimeInterval yz_acceptEventTime; /// 发生时间
- (NSTimeInterval)yz_acceptEventTime {
    return [objc_getAssociatedObject(self, button_acceptEventTime) doubleValue];
}
- (void)setYz_acceptEventTime:(NSTimeInterval)yz_acceptEventTime {
    objc_setAssociatedObject(self, button_acceptEventTime, @(yz_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}