OC 中的成员变量、实例变量和属性

在 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

编译时会报警告:
Autosynthesized property 'myString' will use synthesized instance variable '_myString', not existing instance variable 'myString'

但更换为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来访问。

很多人觉得OC中的点语法比较奇怪,实际是OC设计人员有意为之。点表达式 . 看起来与C语言结构体中的变量访问有点类似,但是这里如果点表达式出现在等号 左边,是调用该属性名称的setter方法;如果点表达式出现在右边,则调用该属性名称的getter方法。 OC中点表达式 . 其实就是调用对象的settergetter方法的一种快捷方式。

虽然现在直接使用@property时,编译器会自动为你生成以下划线开头的成员变量_myString,不需要自己手动再去写实例变量。而且也不在.m文件中通过@synthesize myString;生成setter/getter方法。但在看老代码的时候,我们依旧可以看到有人使用成员变量+ @synthesize 成员变量的形式,我们要明白是 @synthesize 生成了setter/getter方法

注意:新编译器LLVM下的 @property 与 老编译器GCC的 成员变量+ @property + @synthesize 成员变量 是不等价的。

  1. 成员变量+ @property + @synthesize 成员变量的形式,编译器不会帮我们自动生成_成员变量,因此不会操作_成员变量
  2. @synthesize 还有一个作用,可以指定与属性对应的实例变量, 例如@synthesize myString = xxx; ,那么self.myString其实是操作的实例变量xxx,而非_String了。

类与类别中添加的属性要区分来看,因为类别中只能添加方法,不能添加实例变量,也不会自动生成实例变量。经常会在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

这里添加的属性不会自动生成实例变量,这里添加的属性其实是添加的getter与setter方法。注意一点,匿名类别(匿名扩展)是可以添加实例变量的,非匿名类别是不能添加实例变量的,只能添加方法,或者属性(其实也是方法)。

成员变量、实例变量和属性变量的联系

@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文件中。

属性定义:@property(描述1 , 描述2 , 描述3)(class *) varName (不填写取默认值)
描述关键词:nonatomic / atomic,readwrite / readonly,retain / copy / assign

在头文件中 .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文件{ }中添加一个看不可见的_开头的成员变量。

如果在头文件中没有通过@property定义的变量,但是在{}中有定义成员变量,在实现文件中也也没有@sythesize ,那么可以直接通过self->“{}中的变量名”,或者直接使用“{}中的变量名”来访问赋值,这样的变量没有定义setter,getter函数,只能是assign的方式赋值。

再来分析一下@sythesize中的写法,@sythesize abc 直接在.m文件中使用self.abc可以调用成员变量的setter、getter函数,直接调用成员变量名称abc就为访问该变量的指针,对成员变量直接赋值等同于ASSIGN效果。

参考文章 感谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容