作者:代培
地址:http://blog.csdn.net/dp948080952/article/details/52611348
转载请注明出处
这篇博客本来是回答《招聘一个靠谱的iOS》中在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景? 的这一问题,但是写着写着在了解了property的相关内容后,突然冒出了许多对@synthesize使用方法的理解,并且记录了下来,希望分享给大家,与大家一同进步,如果理解的不正确,欢迎在评论区指正,有评论必回!
从前两点来看,当你准备自己手动管理property的所有内容时,编译器就不会帮你自动生成,此时就需要用到@synthesize手动生成(需要注意的是@synthesize必须要写在@implementation中)。
其实对于前两种情况,我觉得是不适合使用@synthesize的,为什么呢?首先要看@synthesize是做什么的,他是帮你自动实现ivar和访问方法,而你已经完全重写了访问方法,此时@synthesize只是让他帮你实现了一个ivar,已经背离了它本身该做的事情,为何不直接自己声明一个ivar呢,同样只要一行代码,这样却更加自然一些。
所以分析到最后,如果想要完全接管@property,最好不要用@synthesize,但是当然你是可以使用@synthesize的,所以这也是@synthesize的一种情况,在这种情况下,@synthesize只有一个作用就是帮你生成ivar。
而且与@synthesize相对的@dynamic,就是让你动态的实现一个属性的访问方法和ivar,如果你想要完全接管其property,最好用@dynamic修饰property,虽然不是必要的,但是显示的声明可以让别人更容易看懂你的代码:你是自己实现的这个属性。
@synthesize还可以用来自定义Property所对应的ivar的名称
假设有一个属性firstName
@synthesize firstName;
上述代码就会生成一个firstName的实例变量与firstName属性相对应。
@synthesize firstName = myFirstName;
该种方式则可以自定义Property所对应的实例变量名称
当然这种方式并不推荐,因为如果大家可能更多使用的是默认的方案,这样所有人写出的代码都更容易理解。
在protocol声明property,让实现这个协议的类共享一些属性,就像协议定义方法一样,目的是只提供一个统一接口的规范,而不给出具体的实现,属性也是相同,属性的实例变量也需要类来自己synthesize,而且编译器不会自动帮你生成,需要注意的是最好不要将Property放在@optional中,因为这样当你没有自己合成ivar时Xcode不会给你警告,而当你在访问这些属性时不会有问题,而当运行时会报 unrecognized selector sent to instance的异常,程序会崩溃。
有两种方式实现protocol中的属性,第一个是使用@synthesize,一句话就可以自动生成ivar和访问方法(当然你也可以重写setter和getter方法)。第二种方法是完全接手,自己声明实例变量(ivar的名称完全可以自己定义,他和属性的关联实际上是在访问方法中关联的),同时实现setter和getter方法(两个方法少一个编译器都会有警告)。总的来说两种方法都可以自定义实例变量名,第一种方法更简便且更清晰,同时只要重写自己需要的定制的访问方法,所以更推荐第一种方法。
这是我在Xcode中重载一个属性时编译器给的警告,警告中很明显的告诉我们这种情况下应该使用@dynamic
Auto property synthesis will not synthesize property ‘xxx’; it will be implemented by its superclass, use @dynamic to acknowledge intention
那到底能不能用@synthesize呢?我们就试试呗
@interface ViewController : UIViewController
@property(nullable, nonatomic, readonly, copy) NSString *nibName;
@end
@implementation ViewController
@synthesize nibName = myNibName;
- (void)viewDidLoad {
[super viewDidLoad];
self.nibName = @"DaiPei";
NSLog(@"the ivar myNibName:%@", myNibName);
}
- (void)setNibName:(NSString * _Nullable)nibName {
myNibName = nibName;
}
- (NSString *)nibName {
return myNibName;
}
@end
新建一个工程,重载ViewController的一个叫nibName的属性(本来重载了其view属性,发现这个vc完全没有用了,连viewDidLoad方法都进不去,然后就挑了一个看似不是很重要的属性)
然后最后打印的结果是the ivar myNibName:DaiPei
,说明重载成功了
那如果把setter和getter方法删去呢?结果是相同的
所以结论是@synthesize可以在重载时使用的,但是我觉得最好的方式还是使用@dynamic,毕竟这是苹果官方推荐的,@synthesize在这里总感觉不是那么优雅!
有一个比较有趣的现象,在头文件中声明的只读property,可以重写其getter方法,而不需要使用@synthesize去生成实例变量,但如果是在.m中声明的property,重写其getter方法时,编译器就不会帮我们自动生成实例变量了。
虽然说.h文件是暴露给外面的一个文件,但在里面声明的readonly属性在类的内部也是无法修改的
如何在内部修改.h中有readonly修饰的属性,有三种方法:
当然这种我这里只是提出@synthesize的这个性质,我并不推荐这种用法,我却觉得这种用法不好,这并不是@synthesize本应该做的事情,如果需要让一个属性在内外有不同表现,第二种方法应该是最好的,显示的表明这个属性的在内外有不同的表现。
有些地方说@synthesize可以用在category中属性的实例变量的合成,我试了一下是不行的,会报错:@synthesize not allowed in a category’s implementation,我不知道以前是不是可以,我的环境是Xcode7,编译器版本:Apple LLVM version 7.3.0 (clang-703.0.29),在category中添加属性只能使用Associated Object来实现,还有一种不优雅的实现方式是用一个单例来保存这些实例遍历。
实际上@dynamic与@synthesize是相对的,也是用来修饰property的,表示实例变量的setter和getter方法手工实现或是在运行时生成。
如果没有ivar的支持,属性单独是没有太多作用的,属性本身的作用我想是在于对实例变量在原子性、读写权限、内存管理、访问方法进行封装,并且声明(注意这里是声明)setter和getter方法(即使你没有实现这两个方法,都在代码里都可以调用这两个方法)。
当你在头文件或是类扩展里声明一个属性时,编译器会在编译期间自动插入一句@synthesize yourProperty = yourProperty;(Objective-C Autosynthesis of Properties,这是clang的支持的特性,实际上自动生成和手动生成效果是相同的)帮你生成一个以开头的同名的实例变量(需要注意的是,前面这句话是不对的,是我最开始的理解,后来我发现自动生成和手动生成有一点不一样,就是自动生成会根据你属性中的修饰词选择性的生成getter和setter方法,而@synthesize没有那么智能,会同时生成setter和getter方法),这句代码同时会按照你声明属性时给出的修饰符自动生成其setter、getter方法,需要注意的是@synthesize做的事情不仅是生成实例变量,还也会生成getter和setter的实现。
所以何时要使用@synthesize?其实很简单,就是在某些编译器无法自动帮你加上这句话的场景下,你自己不想亲自声明ivar实现访问方法时,使用此保留字帮你快捷实现。而实际上这个情况只有一个对于声明在protocol中的property。