Property
干了这么久,我一直觉得自己是个半路出家的IOS开发,虽然是我是正儿八经的科班出生(计算机学院/网络工程专业),但是自己是真的没有接触过ios开发在工作以前,在工作被调来调去以后最终稳定在了IOS开发,然后自己摸爬滚打的开始搞IOS,一边自学一边改项目中的BUG,现在也快要有一年了,感觉自己变成了一个画界面的熟练工,整天在重复的进行一些低复杂度的工作,自己也越来越费,没有一点竞争力。现在我决定开始重新学习Object-C基础。按照《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》这本书的内容重新梳理。希望可以让自己有所成长。
属性介绍:
Objective-C的属性是使用@property语法来声明的公有或私有方法。
@property (readonly, getter=isBlue) BOOL blue;
属性捕获对象的状态。 它们反映了对象的固有属性以及与其他对象的关系。 属性提供了一种安全,便捷的方式来与这些属性进行交互,而不必要手动去编写一组访问方法(如果需要的话,属性也是允许自定义getter和setter方法的)。
我们应当尽可能的使用属性,而不是实例变量。这样有许多的好处:
- 自动合成的getter方法和setter方法。 当我们声明了一个属性,编译器会为我们创建默认的gette方法r和setter方法。
- 更好地声明一组方法。 由于访问方法的命名约定,可以清楚地知道getter和setter的用处。
- 属性的关键词能够传递出相关行为的额外信息。属性提供了一些可能会使用的特质来进行声明,包括assign(vscopy),weak,strong,atomic(vsnonatomic),readwrite,readonly等。
属性方法遵循一个简单的命名约定。 getter方法的名称是属性的名称(属性为date,getter方法也是data),setter方法的名称是set+属性名称,用驼峰命名法编写(setDate)。 BOOL属性的命名约定是用一个以单词“is”开头的命名getter声明它们:
@property (readonly, getter=isBlue) BOOL blue;
如果按照上述的方式声明了属性,那么下面的写法都是可行的:
if (color.blue) { }
if (color.isBlue) { }
if ([color isBlue]) { }
在决定将某些东西声明为属性时,请记住以下几点不可以声明为属性:
init方法;
copy方法,mutableCopy方法;
类工厂方法;
开启某项操作并返回一个BOOL结果的方法;
明确的改变了一个getter的内部状态的副作用方法;
此外,当我们要在自己的代码中使用属性特质时,需要考虑以下的一些规则:
- 一个可读写的属性会有两个访问方法。 setter方法是有一个参数没有返回值的方法,getter方法是一个无参数有一个返回值的方法。如果将这组方法转换成一个属性,就可以用readwrite关键字来标记它(默认特质即为readwrite,可不写)。
- 一个只读属性只有一个访问方法,getter方法,没有参数并返回一个值。 如果我们将这个方法转换为属性,请使用readonly关键字标记它。
- getter方法应当是幂等(idempotent)的(如果一个getter方法被调用两次,那么第二次调用时返回的结果应该和第一调用时返回的结果相同)。然而,如果一个getter方法每次调用时,是被用于计算结果,这是可以接受的。
*幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的方法。
如何使用:
确定一组可以被转换为属性的方法,例如:
- (NSColor *)backgroundColor;
- (void)setBackgroundColor:(NSColor *)color;
然后使用对应关键字的@property语法来声明它们:
@property (copy) NSColor *backgroundColor;
有关属性关键字和其他注意事项的信息,请参考Encapsulating Data。
或者,您可以使用Xcode中的现代Objective-C转换器自动对您的代码进行更改。 有关更多信息,请参考Refactoring Your Code Using Xcode。
以上内容翻译自Adopting Modern Objective-C
举个例子吧,我的理解
@property(nonatomic, copy) NSString* folderName;
下面两个调用方式是一样的(前面是getter方法和setter方法,后面是等价的点方法)
[self setFolderName:@"XiaoMing"] <=> self.folderName = @"XiaoMing";
[self folderName] <=> self.folderName;
setter方法和getter方法实现如下:
//setter方法
- (void)setFolderName:(NSString *)folderName {
_folderName = folderName;
}
//getter方法
- (NSString*)folderName {
return _folderName;
}
其实在整个属性里面最重要的还是属性特质,下面分为几组进行学习:
1.atomic/nonatomic(默认为atomic)
指定合成存取方法是否为原子操作,可以理解为是否线程安全,但在iOS上并不要求属性必须是原子的(atomic),因为这并不能保证线程安全,要保证线程安全需要使用更深层次的锁机制才行。
几乎所有IOS代码的属性设置都会使用nonatomic,这样做的原因是,在iOS中使用同步锁的开销较大,会带来性能问题。
2.readwrite/readonly(默认为readwrite)
readwrite表示自动生成getter方法和setter方法,不需要自己定义。readonly表示只合成getter方法而不合成setter方法。
3.assign、weak、unsafe_unretained
assign表示对属性只进行简单的赋值操作,不更改所赋的新值的引用计数,也不改变旧值的引用计数,常用于标量类型,如NSInteger,NSUInteger,CGFloat,NSTimeInterval等。也可用于修饰NSString,但是容易引发野指针问题,一般不会用于修饰NSString。
使用weak修饰的时候同样不会增加所赋的新值的引用计数,也不减少旧值的引用计数,但当该值被销毁时,weak修饰的属性会被自动赋值为nil,这样就可以避免野指针错误。
使用unsafe_unretained修饰时效果与assign相同,不会增加引用计数,当所赋的值被销毁时不会被置为nil,可能会发生野指针问题。unsafe_unretained与assign的区别在于,unsafe_unretained只能修饰对象,不能修饰标量类型,而assign两者均可修饰。
4.strong、weak
strong表示属性对所赋的值持有强引用表示一种“拥有关系”(owning relationship),会先保留新值即增加新值的引用计数,然后再释放旧值即减少旧值的引用计数。只能修饰对象。如果对一些对象需要保持强引用则使用strong修饰。
weak表示对所赋的值对象持有弱引用表示一种“非拥有关系”(nonowning relationship),对新值不会增加引用计数,也不会减少旧值的引用计数。所赋的值在引用计数为0被销毁后,weak修饰的属性会被自动置为nil,能够有效防止野指针错误。
weak常用在修饰delegate等防止循环引用的场景。
5.copy
copy修饰的属性会在内存里拷贝一份对象,两个指针指向不同的内存地址。一般用来修饰有对应可变类型子类的对象,比如NSString,NSArray等。为确保这些不可变对象因为可变子类对象影响,需要copy一份备份,如果不使用copy修饰,使用strong或assign等修饰则会因为多态导致属性值被修改。
参考文章:
https://www.jianshu.com/p/646ae400fe7b
更加深入理解的文章: