1、成员变量、实例变量
@interface ViewController (){
NSString *_type;
NSString *_gender;
NSInteger _age;
}
@property(nonatomic,copy) NSString *name;
@end
{ }中声明的变量是成员变量,@property申明的是属性,那么实例变量又是什么?变量类型为对象的成员变量就是实例变量,_type、_gender就是实例变量,实例变量是成员变量的特殊情况
成员变量 = 基本数据类型变量 + 实例变量
成员变量 : 用于类内部,因为成员变量不会生成setter、getter方法,所以外界无法访问成员变量。
成员变量访问方式如下:
_type = @"ttt";
NSLog(@"_type ==== %@",self->_type);
给_type赋值,通过_type、self->_type两种方式都是OK的
2、属性
@property申明的是属性
2.1、Class中的property
编写如下代码:
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"123";
_name = @"123";
}
- (void)setName:(NSString *)name{
_name = name;
NSLog(@"setName 被调用%s",__func__);
}
给name赋值,可以通过self.name、_name 两种方式进行访问
通过self.name访问属性时,setName会被调用,而通过_name访问属性,setName不会被调用
为什么通过self.name访问属性时,setName会被调用?
在oc中点表达式其实就是调用对象的setter和getter方法的一种快捷方式
如果点表达式出现在等号左边,该属性名称的setter方法将被调用,如果点表达式出现在等号右边,该属性名称的getter方法将被调用。
为什么可以通过_name进行访问呢?
iOS5之前声明一个能被外面访问的属性,必须声明属性跟与之对应的成员变量,setter、getter方法
@interface ViewController (){
NSString *_name;
}
@property(nonatomic,copy) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setName:(NSString *)name{
_name = name;
NSLog(@"setName 被调用%s",__func__);
}
- (NSString *)name{
NSLog(@"getName 被调用%s",__func__);
return _name;
}
但是iOS5之后,苹果是建议以下的方式来使用:
@interface ViewController ()
@property(nonatomic,copy) NSString *name;
@end
什么都不写,默认生成成员变量_name、setter、getter方法
由此可见:property的本质 = ivar(成员变量) + getter + setter;
因为编译器会自动编写访问属性所需的方法,此过程叫做“自动合成”( auto synthesis)。需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法” (synthesized method)的源代码。除了生成方法代码之外,编译器还要自动向类中添加适当类型的成员变量,并且在属性名前面加下划线,以此作为成员变量的名字。
此外可以通过 @synthesize语法来指定成员变量的名字,比如你想给name属性的成员变量改一下名字,不再使用_name进行访问,那么可以用@synthesize name = _newName;之后使用_name进行访问就会报错,因为name属性的成员变量名称是_newName咯。
@synthesize name; 就是将name属性的成员变量命名为name
但是,增加一个属性_newName,此时就会出现警告,属性_newName不能自动合成,因为已经存在一个名字相同的成员变量咯

此时通过self._newName = @"123"进行赋值,就会出现崩溃,[ViewController set_newName:]: unrecognized selector sent to instance 0x7faeb241b2e0因为属性_newName没有自动合成,点语法又是访问setter方法,setter方法根本不存在,
总结下 @synthesize 合成成员变量的规则,有以下3点:
- 如果指定了的名称,会生成一个指定名称的成员变量;
- 如果没有指定成员变量的名称,会自动生成一个属性同名的成员变量;
- 如果这个成员变量已经存在,就不会自动合成
2.2、Protocol、Category中的property
一般情况下,不能向存在的类添加属性,因为property的本质 = ivar(成员变量) + getter + setter;
runtime库中,class_addIvar函数用于给类添加成员变量,但是文档中特别说明:
@note This function may only be called after objc_allocateClassPair and before objc_registerClassPair.
* Adding an instance variable to an existing class is not supported.
意思是这个函数只能在类创建(objc_allocateClassPair)类注册(objc_registerClassPair)之间使用,往已经的类中添加实例变量是不支持的,经过编译的类在程序启动后就已经加载runtime,没有机会调用addIvar,所以没有机会再添加实例变量,也就是不能添加属性了。
在protocol 和 category 中如何使用 property?
在protocol 中使用 property 只会生成setter 和 getter 方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性
category 使用 property 也是只会生成 setter和 getter 方法的声明,如果我们真的需要给 category增加属性的实现,需要借助于runrime的两个函数:objc_setAssociatedObject 和 objc_getAssociatedObject
如给Person类创建一个分类,增加gender属性
#import "Person.h"
NS_ASSUME_NONNULL_BEGIN
@interface Person (gender)
@property (nonatomic , copy) NSString *gender;
@end
NS_ASSUME_NONNULL_END
#import "Person+gender.h"
#import <objc/message.h>
@implementation Person (gender)
- (void)setGender:(NSString *)gender{
objc_setAssociatedObject(self, @"gender", gender, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)gender{
return objc_getAssociatedObject(self, @"gender");
}
@end
3、@synthesize和@dynamic分别有什么作用?
-
@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var; -
@synthesize: 如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。 -
@dynamic: 告诉编译器属性的setter与getter方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没问题,但是当程序运行到instance.var = someVar,由于缺setter方法会导致程序崩溃;或者当运行到someVar = var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
参考地址:
iOS中属性与成员变量的区别
招聘一个靠谱的iOS