理解 Objective-c "属性"
@property 是OC中能够快速定义一个属性的关键字,如下我们定义一个属性。
@property NSString *String;
这样我们就可以使用这个属性
属性的实质
- 在属性被加入OC以前我们每次声明一个实例变量都要自己声明并实现存取方法
//// Created by chao on 15/8/29.// Copyright (c) 2015 ___FULLUSERNAME___. All rights reserved.//#import@interface Person : NSObject{ NSString *firstName; NSString *lastName;}- (void)setFirstName:(NSString *)first;- (NSString *)firstName;- (void)setLastName:(NSString *)last;- (NSString *)lastName;@end//在.m文件里实现#import "Person.h"@implementation Person {}- (void)setFirstName:(NSString *)first { firstName = [first copy];}- (NSString *)firstName { return firstName;}- (void)setLastName:(NSString *)last { lastName = [last copy];}- (NSString *)lastName { return lastName;}@end
现在有了@property
只要简单的声明一下就可以让编译器替我们做以上哪些繁杂的工作。
@property NSStrinng *firstNmae;@property NSString *lastName;
- 声明属性 编译器除了生成存取方法代码外,还要自动向类中添加适当类型的实例变量。
编译器会自动在属性名之前添加下划线,一次作为实例变量的名字,在上面的声明中会生成两个实例变量
_firstNam, _lastName.我们也可以使用@synthesize语法指定实例变量的名字。@synthesize firstName = _myFirstName; //使用指定的实例变量名称@synthesize lastName = _myLastName;//如果没有特殊需要尽量使用系统默认的名称
- 如果不想令编译器合成存取方法, 则可以自己实现,如果只实现了其中一个存取方法, 则另外一个还是会由编译器重新合成,你可以使用@dynamic 关键字阻止编译器自动合成存取方法。
@dynamic关键字
@property NSStrinng *firstNmae;@property NSStrinng *firstNmae;@dynamic firstName, lastname; //编译器不会自动为这两个属性合成存取方法,或实例变量。
属性的特性
存取类型
- readwrite (默认)编译器自动创建存方法和取方法
- readonly 不生成存方法,如果一个属性不允许修改则可以将其声明为存方法。
在为以上类添加一个只读的ID和weight属性。
@property (readonly) NSInteger ID;@property (readwrite) NSInteger height;
如果我们这程序中试图修改person 的ID属性编译器就会报错
生命周期类型(内存管理类型)
生命周期类型的特性包括, assign, strong, weak和copy 这些特性决定了存方法如何处理与其相关的内存管理问题
- assign (默认)最简单的,存入的值会将传入的值直接赋给实例变量。
@property (assign) NSInteger ID;这段代码等同于实现了一下存方法- (void)setID:(NSInteger)d { ID = d; }
- strong 特性要求保留传入的对象,并放弃原有对象(如果原有对象不在有其他拥有方,就会被释放)。凡是指向对象的实例变量,通常都应该使用strong
- weak 要求不保留传入的对象,相应的存取方法会将传入的对象直接赋给实例变量。如果该对象被释放,那么相应的实例变量会呗自动置nil
- copy 特性要求拷贝传入对象,并将新对象赋给实例变量。
copy 详解
看了很多博客讲解的copy都只是简单的说了一下,copy特性要求拷贝传入对象。并没有进行深入的讲解,比如为什么要copy传入对象,下面我写一下我自己对copy的理解
- 在OC中有些类会有特定的可修改的子类,比如 像NSString 和 NSMutableString 这些类我们平常使用时一般都声明为copy。像下面我们定义一个NSString 属性,但是给其存方法传入 NSMutableString 是合法的,声明为 copy,就可以避免修改 原对象对实例变量的影响
@property (strong) NSString *firstName;@property (copy) NSString *lastName;NSMutableString *name = [[NSMutable alloc] initWithString:@"Li"];[person setFirstNmae:name];[person setLastName:name];//这样修改name 不会对实例变量产生影响。//看以下程序的输出 NSMutableString *firstName = [NSMutableString stringWithString:@"Zhang"]; NSMutableString *lastName = [NSMutableString stringWithString:@"San"];person.firstName = firstName;person.lastName = lastName;NSLog(@"修改前的 :%@%@", person.firstName, person.lastName);[firstName appendString:@"fe"];[lastName appendString:@"aefa"];NSLog(@"修改后的 :%@%@", person.firstName, person.lastName);NSLog(@"%@%@", firstName, lastName);
2.如果传入的对象是不可修改的,copy方法实际是在调用copyWithZone:一般我们自定义的对象如果要求copy,应该重写 copyWithZone:方法从而优化copy过程
- (id)copyWithZond { return self;}
- copy返回的值总是不可修改的,如果需要copy出可修改的对象,需要使用对应的mutableCopy方法
atomic 和nonatomic
- 如果不声明默认是atomic,atomic属性的存方法并不会对多线程下的安全有太大帮助,所以一般用nonatomic,而且atomic会影响性能。(确保线程安全必定会有额外开销)