1. 概念与区别:
@interface xx(){“成员变量”} -> { }包裹的是成员变量—基本数据类型(int/float)+实例变量(NSString)
实例变量+基本数据类型变量=成员变量
- 属性变量:声明标识符,对set/get方法声明,并不是变量
属性并不是变量,是Objective-C 的一项特性,是属性的声明标识符,提供成员变量的访问方法的声明、控制成员变量的访问权限、控制多线程时成员变量的访问环境,主要的作用就在于封装对象中的数据,Objective-C 对象通常会把其所需要的数据保存为各种实例变量,实例变量一般通过“存取方法”(access method)来访问。@property 本质
成员变量一种,不包含基本数据类型,@synthesize name = _name , _name实例变量。
@interface MM (){ int _age;//成员变量 NSString * _name;//成员变量 } @property (nonatomic,assign) int age;//属性,set/get方法声明,标识符 nonatomic:权限 @end
@implementation MM @synthesize age = _age;//生成实例变量_age,生成set/get方法,并将set/get方法作用于实例变量
- (void)setAge:(int)age{//对_age的封装,便于子类和外部访问,同时设置访问权限等 _age = age; } - (int)age{ return _age; } @end
|
2. 历史和关系:
详情参考:IOS属性的作用
GCC
手动声明实例变量
***.h*** @interface ViewController : UIViewController { //属性的实例变量 NSString *myTitle; } //编译器遇到@property会自动声明对应的setter/getter @property (copy, nonatomic) NSString *myTitle; @end
***.m*** //编译器遇到@synthesize会自动实现setter/getter方法 //编译器遇到@synthesize回去访问myTitle的同名变量,如果没找到就报错。 @synthesize myTitle;
|
LLVM
编译器就可以自动为属性生成一个名字带下划线的实例变量
属性改变步骤:
- 第一版,最早版本
***.h*** @interface ViewController : UIViewController { NSString *myTitle; } //编译器遇到@property会自动声明对应的setter/getter @property (copy, nonatomic) NSString *myTitle; @end
***.m*** //编译器遇到@synthesize会自动实现setter/getter方法 //编译器遇到@synthesize回去访问myTitle的同名变量,如果没找到就报错。 @synthesize myTitle;
|
2.第二版,不需要自己手动声明一个成员变量,关键字@synthesize会默认去访问myTitle的同名变量,如果找不到则会自动生成一个
***.h*** @interface ViewController : UIViewController @property (copy, nonatomic) NSString *myTitle; @end
***.m*** /* 1.编译器遇到@synthesize会自动实现setter/getter方法; 2.编译器遇到@synthesize回去访问myTitle的同名变量; 3.如果找不到同名的变量,会自动生成一个私有同名变量myTitle 4.因此现在开始就不用再手动声明一个实例变量了, */ @synthesize myTitle;
|
3.第三版,考虑到变量名和getter的方法名、setter的参数名一样,容易让人误会,引起警告,所以默认给属性生成的同名变量名带下划线。
/* 1.指定_myTitle作为myTitle的实例变量 2.这样我们就可以去使用_myTitle,避免和setter名同名产生误会了 */ @synthesize myTitle = _myTitle;
|
4.第四版,在IOS4.5后,也@synthesize可以省略了,最终就变成这样了
@property (copy, nonatomic) NSString *myTitle;
|
如果我们把上面的过程全部显示出来的话,本质如下:
**.h** #import <UIKit/UIKit.h>
@interface ViewController : UIViewController { /* ***被隐藏的代码:*** 1.这个默认是@synthesize myTitle = _myTitle;生成的 2.所以如果我们手动设置@synthesize myTitle,那么我们编译器 生成的变量就是NSString *myTitle,相当于@synthesize myTitle = myTitle, 如果设置@synthesize myTitle = youTitle,那么编译器生成的变量就是NSString *youTitle了 这要注意。 */ NSString *_myTitle; } @property (copy, nonatomic) NSString *myTitle; //***被隐藏的代码*** //编译器遇到@property会自动声明setter/getter方法 - (void)setMyTitle:(NSString *)myTitle; - (NSString *)myTitle; @end
**.m** /* ***被隐藏的代码*** 1.@synthesize关键字会自动实现setter/getter的方法 2.@synthesize myTitle = _myTitle指明了属性myTitle的实例变 量是_myTitle,setter/getter操作的对象就是_myTitle.
*/ @synthesize myTitle = _myTitle; - (void)viewDidLoad { [super viewDidLoad]; _myTitle = @"123";
}
//***被隐藏的代码*** //由关键字@synthesize自动实现 - (NSString *)myTitle{ return _myTitle; } - (void)setMyTitle:(NSString *)myTitle{ _myTitle = myTitle; }
|
代码说明:
- 编译器遇到关键字
@property
,自动声明setter/getter方法。
- 编译器遇到
@synthesize
,自动实现setter/getter方法。
@synthesize myTitle = _myTitle;
为属性 myTitle
生成了一个实例变量 _myTitle
,所以我们对属性的操作self.myTitle
实质上都是在操作_myTitle
变量。
所以属性、实例变量和成员变量的关系是:
声明属性的时候编译器自动生成的实例变量,实例变量的本质就是成员变量,self.myTitle
操作属性的时候实质上是在操作成员变量 _myTitle
(也就是实例变量)。
问题:
- 我们能否认为新编译器LLVM下的@property == 老编译器GCC的 成员变量+ @property + @synthesize 成员变量呢?
答案是否定的,
因为成员变量+ @property + @synthesize 成员变量的形式,编译器不会帮我们生成成员变量,因此不会操作成员变量了(注:区别就是LVVM操作实际是_成员变量,GCC操作成员变量,这样来说并不==);
同时@synthesize 还有一个作用,可以指定与属性对应的实例变量,
例如@synthesize myString = xxx;
那么self.myString其实是操作的实例变量xxx,而非_String了。
详情参考:成员变量和属性的前世今生
- 同时重写
setter/getter
的问题
我们会发现,当我们同时重写 setter/getter
时会报错,为什么呢?这是因为当我们同时重写 setter/getter
时,编译器自动添加的代码@synthesize myTitle = _myTitle;
失效,就不会自动为我们生成实例变量_myTitle
了,setter/getter
操作的对象就不存在了。所以我们要加上@synthesize myTitle = _myTitle;
,手动指定setter/getter要操作的实例对象是_my
参考:
注:参考价值排序