在 iOS 5 之后,苹果推出的一个新机制,@property
声明的属性默认会生成一个带下划线_
类型的成员变量,同时也会生成相应的setter/getter
方法。 但是我们在看老代码时,经常看到以下这样的代码:
如下:
@interface ViewController () {
// 1.声明成员变量
NSString *myString;
}
//2.在用@property
@property(nonatomic, copy) NSString *myString;
@end
@implementation ViewController
//3.最后在@implementation中用synthesize生成set方法
@synthesize myString;
@end
一个大括号里面定义了成员变量,同时用了 @property
声明,而且还在 @implementation
中使用 @synthesize
方法。其实,发生这种状况根本原因是苹果将默认编译器从GCC转换为LLVM(low level virtual machine
),才不再需要为属性声明实例变量了。
在没有更改之前,属性的正常写法需要成员变量+ @property + @synthesize 成员变量
三个步骤。
如果我们只写成员变量+ @property
:
@interface GBViewController : UIViewController {
NSString *myString;
}
@property (nonatomic, strong) NSString *myString;
@end
但更换为LLVM之后,编译器在编译过程中发现没有新的实例变量后,就会生成一个以_
开头的实例变量。因此现在我们不必在声明一个实例变量(是不必要,不是不可以) 。
当然我们也熟知,@property
声明的属性不仅仅默认给我们生成一个_
开头的成员变量,同时也会生成setter/getter
方法。
在.m
文件中,编译器也会自动的生成一个实例变量 _myString
。那么在.m文件中可以直接的使用 _myString
成员变量,也可以通过属性 self.myString
都是一样的。
注意:这里的 self.myString
其实是调用的 myString
属性的 setter/getter
方法。
这与 C++ 中点的使用是有区别的,C++ 中的点可以直接访问成员变量(也就是实例变量)。
例如,在 OC 中有如下代码:
@interface MyViewController : UIViewController {
NSString *name;
}
@end
在这段代码里面只是声明了一个成员变量,并没有setter/getter
方法。所以访问成员变量时,可以直接访问name
,也可以像C++一样用self->name
来访问,但绝对不能用self.name
来访问。
虽然现在直接使用@property
时,编译器会自动为你生成以下划线开头的成员变量_myString
,不需要自己手动再去写实例变量。而且也不在.m文件中通过@synthesize myString;
生成setter/getter
方法。但在看老代码的时候,我们依旧可以看到有人使用成员变量+ @synthesize 成员变量
的形式,我们要明白是 @synthesize
生成了setter/getter
方法。
类与类别中添加的属性要区分来看,因为类别中只能添加方法,不能添加实例变量,也不会自动生成实例变量。经常会在iOS 代码中看到在类别中添加属性,比如在UINavigationController.h 文件中对 UIViewController 类进行的扩展:
@interface UIViewController (UINavigationControllerItem)
@property(nonatomic,readonly,strong) UINavigationItem *navigationItem; // Created on-demand so that a view controller may customize its navigation appearance.
@property(nonatomic) BOOL hidesBottomBarWhenPushed API_UNAVAILABLE(tvos); // If YES, then when this view controller is pushed into a controller hierarchy with a bottom bar (like a tab bar), the bottom bar will slide out. Default is NO.
@property(nullable, nonatomic,readonly,strong) UINavigationController *navigationController; // If this view controller has been pushed onto a navigation controller, return it.
@end
成员变量、实例变量和属性变量的联系
@interface MyViewController : UIViewController {
UIButton *yourButton;
int count;
id data;
}
@property (nonatomic, strong) UIButton *myButton;
@end
在{ }
中所声明的变量都为成员变量。 所以yourButton、count、data
都是成员变量。实例变量本质上就是成员变量,只是实例是针对类而言,实例是指类的声明。{ }
中的yourButton
就是实例变量。
成员变量用于类内部,无需与外界接触的变量。因为成员变量不会生成set、get方法,所以外界无法与成员变量接触。根据成员变量的私有性,为了方便访问,所以就有了属性变量。属性变量的好处就是允许让其他对象访问到该变量(因为属性创建过程中自动产生了set 和get方法)。当然,你可以设置只读或者可写等,设置方法也可自定义。所以,属性变量是用于与其他对象交互的变量。
综上所述可知:成员变量是定义在{ }
号中的变量,如果变量的数据类型是一个类则称这个变量为实例变量。因为实例变量是成员变量的一种特殊情况,所以实例变量也是类内部使用的,无需与外部接触的变量,这个也就是所谓的类私有变量。而属性变量是用于与其他对象交互的变量。
但是,现在大家似乎都不怎么喜欢用成员变量来定义类的变量,都喜欢用属性来定义类的变量。把需要与外部接触的变量定义在.h文件中,只在本类中使用的变量定义在.m文件中。
在头文件中 .h 一般在 { }
里面会有定义的实例变量,示例:
//// .h中
@property (automic,retain) NSString *abc;
//// .m中
@sythesize abc;
在写了@sythesize abc;
的情况下,系统不会自动生成实例变量_abc
,直接通过变量名abc
,也就是直接使用变量名、self->变量名
在赋值运算的时候(=号左边),只是将内存区域的指针赋值给变量,访问的变量相当于assgin
型,原对象的引用计数器不会发生变化。如果是通过点语句 self.abc =
来赋值,就要看在 @property
中定义的是copy、retain、assign
哪一种了,如果没有加上上述描述词,就默认为assign
。如果没有写@sythesize abc;
系统会默认自动在.h文件{ }
中添加一个看不可见的_
开头的成员变量。
参考文章 感谢!