“属性”(property)是OC的一项特性,用于封装对象中的数据。安全
@property
@Property是声明属性的语法(@property = ivar + getter + setter
)。多线程
OC对象一般会把其所需的数据保存为各类实例变量(ivar
)。实例变量通常经过“存取方法”(accessmethod
)来访问。性能
什么是存取方法:
getter
和setter
方法(access method = getter + setter
),其中getter
用于获取变量value
, 而setter
用于写入value
。atom
@Property
能够快速方便的为实例变量建立存取器。线程
// Man.h #import <Foundation/Foundation.h> @interface Man : NSObject @property (nonatomic,strong)NSString *name; @property (nonatomic,strong)NSString *sex; @end
与下面的写法等效指针
// Man.h #import <Foundation/Foundation.h> @interface Man : NSObject { // 实例变量 NSString *name; NSString *sex; } // setter - (void)setName:(NSString *)newName; // getter - (NSString *)name; // setter - (void)setSex:(NSString *)newSex; // getter - (NSString *)sex; @end
一般使用“点语法” 来让编译器自动调用相关的存取方法(access method = getter + setter
)。code
self. name = @"sky"; NSString *name = self. name;
点语法有什么优点呢?对象
省时,省力 :若是使用了属性,编译器会自动编写访问属性所需的方法。这个过程由编译器在编译期执行,看不到这些
get set
源代码。内存编译器会自动向类中添加适当类型的实例变量,而且在属性名前添加下划线。开发
若是你不想让编译器自动合成存取方法,则能够本身实现。若是你只实现了其中一个存取方法,那么另外一个仍是会由编译器来合成。
当咱们同时重写了setter and getter
方式时,系统会报错,缘由是找不到实例变量。其解决方法: 在.m
的文件中使用@synthesize
。
@synthesize
@synthesize
是为属性添加一个实例变量名,或者说别名。同时会为该属性生成 setter/getter
方法。
在protocol
中使用property
只会生成setter
和getter
方法声明,咱们使用属性的目的,是但愿遵照我协议的对象能实现该属性。须要使用@synthesize
生成setter
和getter
。
当你在子类中重载了父类中的属性,你必须 使用@synthesize
来手动合成ivar
。
当咱们同时重写了setter and getter
方式时,须要在.m的文件中使用@synthesize
。
// Man.m #import "Man.h" @implementation Man @synthesize name = _name; // setter - (void)setName:(NSString *)name { _name = name; } // getter - (NSString *)name { return _name; } @end
**@synthesize name = _name**
_name
是成员变量
name
是属性
做用是告诉编译器name
属性为_name
实例变量生成setter and getter
方法的实现
name
属性的setter
方法是setName
,它操做的是_name
这个变量
在@synthesize
中定义与变量名不一样的setter
和getter
的命名,以此来保护变量不会被不恰当的访问(setter=<name>
这种不经常使用,也不推荐使用)
//setter=<name>这种不经常使用,也不推荐使用 @property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name; @property (nonatomic,getter = isHidden ) BOOL hidden;
@property
有两个对应的词,一个是 @synthesize
,一个是 @dynamic
。若是 @synthesize
和 @dynamic
都没写,那么默认的就是@syntheszie var = _var
。
若是某属性已经在某处实现了本身的 setter/getter
,可使用 @dynamic
来阻止 @synthesize
自动生成新的 setter/getter
覆盖。
@dynamic
@dynamic
告诉编译器:属性的 setter
与 getter
方法由用户本身实现,不自动生成。(固然对于 readonly
的属性只需提供 getter
便可)。
假如一个属性被声明为 @dynamic var
,而后你没有提供@setter
方法和 @getter
方法。编译的时候没问题,可是当程序运行到 instance.var = someVar
,因为缺 setter
方法会致使程序崩溃。或者当运行到 someVar = var
时,因为缺 getter
方法一样会致使崩溃。
编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
// Man.h #import <Foundation/Foundation.h> @interface Man : NSObject @property (nonatomic,strong)NSString *name; @end // Man.m #import "Man.h" @implementation Man @dynamic name; // setter // - (void)setName:(NSString *)name // { // _name = name; // } // getter - (NSString *)name { return _name; } @end
调用时会出现崩溃
Man *man = [[Man alloc] init]; man.name = @"sky";//缺 setter 方法会致使程序崩溃 NSString *name = man.name;//缺 getter 方法一样会致使崩溃
atomic
(默认):atomic
意为操做是原子的,意味着只有一个线程访问实例变量(生成的setter
和getter
方法是一个原子操做)。atomic
是线程安全的,至少在当前的存取器上是安全的。它是一个默认的特性,可是不多使用,由于比较影响效率。
nonatomic
:nonatomic
意为操做是非原子的,能够被多个线程访问。它的效率比atomic
快。但不能保证在多线程环境下的安全性,开发中经常使用。
开发iOS程序时应该使用nonatomic
属性,由于atomic
(同步锁)属性严重影响性能。该属性使用了同步锁,会在建立时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,经过声明nonatomic
能够节省这些虽然很小可是没必要要额外开销。
readwrite
(默认):readwrite
是默认值,表示该属性同时拥有setter
和getter
。
readonly
: readonly
表示只有getter
没有setter
。
有时候为了语意更明确可能须要自定义访问器的名字。
//setter=<name>这种不经常使用,也不推荐使用 @property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name; @property (nonatomic,getter = isHidden ) BOOL hidden;
assign
(默认):assign
用于非指针变量(值)类型,统一由系统栈进行内存管理。通常用于基础类型和C
数据类型,如int
、float
、double
和NSInteger
,CGFloat
等表示单纯的复制。还包括不存在全部权关系的对象,好比常见的delegate
。
retain
:在setter
方法中,须要对传入的对象进行引用计数加1
的操做。
strong
:strong
是在IOS
引入ARC
的时候引入的关键字,是retain
的一个可选的替代。对传入的对象的强引用,会增长对象的引用计数。strong
跟retain
的意思相同并产生相同的代码,可是语意上更好更能体现对象的关系。
weak
:对传入的对象的弱引用,不增长对象的引用计数,也不持有对象,当对象消失后指针自动指向nil
。
copy
:与strong
相似,但区别在于copy
是建立一个新对象,strong
是建立一个指针,引用对象计数加1
。
weak
与strong
与copy
属性特质的差别Person
类的实例变量,并分别用weak
与strong
修饰。@property (nonatomic,strong) Person *strongPerson; @property (nonatomic,weak) Person *weakPerson;
strongPerson
属性置nil
。self.strongPerson = [[Person alloc] init]; self.weakPerson = self.strongPerson; self.strongPerson = nil; NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
输出结果为:strongStr=(null),weakStr=(null)
。说明weak修饰的属性并不会使引用计数增长。
@property (nonatomic,strong) NSString *strongStr; @property (nonatomic,weak) NSString *weakStr; ··· self.strongStr = @"string"; self.weakStr = self.strongStr; self.strongStr = nil; NSLog(@"strongStr=%@,weakStr=%@",self.strongStr,self.weakStr);
输出结果为:strongStr=(null),weakStr=string
。这里主要是由于NSString类型的赋值默认会加上copy,而copy会建立一个新的对象。这里的赋值语句实际上是
self.strongStr = [@"string" copy]; self.weakStr = [self.strongStr copy];
weakPerson
属性置nil
。self.strongPerson = [[Person alloc] init]; self.weakPerson = self.strongPerson; self.weakPerson = nil; NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
输出结果以下:strongStr=<Person: 0x600000007d50>,weakStr=(null)
。说明weak修饰的属性只是对对象的弱引用,并不会真正的持有该对象。
Person
类实例变量p
,赋值strongPerson
后将p
置nil
。Person *p = [[Person alloc] init]; self.strongPerson = p; self.weakPerson = self.strongPerson; p = nil; NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
输出结果为:strongStr=<Person: 0x600000200b50>,weakStr=<Person: 0x600000200b50>
。由于strong
属性会强引用该对象并使该对象的引用计数+1
,因此即便把p
设置为nil
,该对象也并无释放,要想释放该对象,还得把strongStr
设置为nil:self.strongPerson = nil;
。这样输出结果才为 strongStr=(null),weakStr=(null)
。
Person
类加了一个name
属性。并用copy
修饰 :(@property (nonatomic,copy) NSString *name
)。NSString *a = @"xiaoming"; Person *p = [[Person alloc] init]; p.name = a; NSLog(@"before p.name=%@",p.name); a = @"xiaohua"; NSLog(@"after p.name=%@",p.name);
输出结果:before p.name=xiaoming
与after p.name=xiaoming
。由于copy
关键字修饰的属性是将对象拷贝一份赋值,因此你改变原对象并不会对拷贝后的对象有任何改变。
注:用@property
声明 NSString
、NSArray
、NSDictionary
常用copy
关键字,是由于他们有对应的可变类型:NSMutableString
、NSMutableArray
、NSMutableDictionary
,他们之间可能进行赋值操做,为确保对象中的字符串值不会无心间变更,应该在设置新属性值时拷贝一份.
能够用@property
语法来定义对象中所封装的数据。
经过“修饰词”来指定存储数据所需的正确语义。
在设置属性所对应的实例变量时,必定要听从该属性所声明的语义。
开发iOS程序时应该使用nonatomic
属性,由于atomic
(同步锁)属性严重影响性能。