iOS 成员变量、实例变量、属性解析

2018-12-19  本文已影响0人  小秀秀耶

1、成员变量、实例变量

@interface ViewController (){
    NSString *_type;
    NSString *_gender;
    NSInteger _age;
}

@property(nonatomic,copy) NSString *name;

@end

{ }中声明的变量是成员变量,@property申明的是属性,那么实例变量又是什么?变量类型为对象的成员变量就是实例变量,_type_gender就是实例变量,实例变量是成员变量的特殊情况

成员变量 = 基本数据类型变量 + 实例变量

成员变量 : 用于类内部,因为成员变量不会生成settergetter方法,所以外界无法访问成员变量。

成员变量访问方式如下:

 _type = @"ttt";
 NSLog(@"_type ==== %@",self->_type);

_type赋值,通过_typeself->_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中点表达式其实就是调用对象的settergetter方法的一种快捷方式

如果点表达式出现在等号左边,该属性名称的setter方法将被调用,如果点表达式出现在等号右边,该属性名称的getter方法将被调用。

为什么可以通过_name进行访问呢?

iOS5之前声明一个能被外面访问的属性,必须声明属性跟与之对应的成员变量,settergetter方法

@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

什么都不写,默认生成成员变量_namesettergetter方法

由此可见: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点:

  1. 如果指定了的名称,会生成一个指定名称的成员变量;
  2. 如果没有指定成员变量的名称,会自动生成一个属性同名的成员变量;
  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,所以没有机会再添加实例变量,也就是不能添加属性了。

protocolcategory 中如何使用 property?

protocol 中使用 property 只会生成settergetter 方法声明,我们使用属性的目的,是希望遵守我协议的对象能实现该属性

category 使用 property 也是只会生成 settergetter 方法的声明,如果我们真的需要给 category增加属性的实现,需要借助于runrime的两个函数:objc_setAssociatedObjectobjc_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分别有什么作用?

  1. @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
  2. @synthesize : 如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
  3. @dynamic : 告诉编译器属性的 settergetter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

参考地址:
iOS中属性与成员变量的区别
招聘一个靠谱的iOS

上一篇下一篇

猜你喜欢

热点阅读