随着苹果将默认的编译器从GCC换成了LLVM,编译器自动为一个属性生成对应的变量和setter/getter方法,我们已经越来越习惯在自己创建的类中直接通过声明一个属性来创建一个类变量了。比如:
#import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, strong) NSString* name; @property (nonatomic) int age; -(void)func; -(instancetype)init; -(void)setName:(NSString *)name; -(NSString*) name; @end
但是却忽视了属性和变量本质上的区别,导致在使用中遇到很多奇怪的问题无法理解,尤其是在非ARC下这种情况就更是如此。
在最早的iOS编程中,我们必须为每一个属性声明一个对应的变量,两者相互配合使用,比如:
@interface MyViewController :UIViewController { UIButton *myButton; } @property (nonatomic, retain) UIButton *myButton; @end
但是现在我们并不需要这么写,只需要声明一个属性比如name,那么编译器就会自动为其声明一个对应的变量_name。在我看来,所谓属性,其实本质还是对变量的管理,属性可以看做是对变量的封装,通过封装变量一来可以方便开发,省略了大量的setter/getter方法的编写。同时也避免了直接操作变量的不安全性。在此基础之上,属性还有多个附加特性:nonatomic、strong、weak、copy、atomic等等,使得原本复杂的线程安全、retain/release机制等都可以通过简单的属性申明而避免,大大方便了程序开发。由此看来,属性确实是Objective-c相对于其他语言一个重要的优势。
变量
Objective-c下传统的变量并没有太多的区别:公有、私有以及保护变量:
#import <Foundation/Foundation.h> @interface Person : NSObject { @private int privateVar; @public int publicVar; @protected int protectedVar; } @end
其含义和C++、Java等语言也一样:公有变量可以在类外部访问;私有变量仅能在类内部访问且不能被子类访问;保护变量仅能在类内部和子类访问。在Objective-c中访问类变量的语法是:self->privateVar。注意必须要使用类似C++下指针的访问方式,如果使用.语法是无法访问变量的。使用诸如self->privateVar的语法访问变量就是直接操作变量,这时变量操作的安全性都是由编码者自己负责。尤其是在操作涉及到内存操作的时候需要格外小心,最典型的使用场景就是在对诸如NSString*等类型的变量进行赋值操作时候的retain和release操作:
-(void)setPrivateVar:(NSString*)var { if(self->privateVar == var) return; [var retain]; [self->privateVar release]; self->privateVar = var; return; }
点语法
还有一点值得注意的就是,在使用属性之后,编译器不仅为对应的变量自动生成setter/getter方法,同时还简化了调用两种方法的方式:点语法。在Objective-c中p.name = @"liyazhou",这样一个语句并不是像在c++或者java下直接操作一个实例的类成员变量,而实际上是调用了对应的setter方法,所以p.name = @"liyazhou"调用的语句是[p setName:@"liyazhou"]。而return p.name实际上等价于return [p getName];
以上就是Objective-c下面属性和变量最基本的区别和联系。总的来说属性的出现大大简化了Objective-c的编程,但同时也使初学者混淆了相关概念,稀里糊涂之下随便使用,知其然不知其所以然,遇到问题不知所措。所以想要放心大胆地使用一些便利而强大的编程特性就必须对其背后的原理烂熟于胸才行。