iOS学习——属性引用self.xx与_xx的区别
在iOS开发过程中,我们用@proprety声明一个属性后,在代码中我们可以用self.xx与_xx来获取到这个属性。但是一直有一个疑惑,那就是这两个之间有什么区别呢?最初我一直觉得这两个之间没什么区别的,直到有一次,我发现自己明明对声明的属性进行了赋值,但是在使用_xx引用时发现为nil,这才引起我的注意。所以,今天在这里对这个问题进行统一的一个说明和学习。
1 @property 与 @synthesize
在说self.xx与_xx之前,我们先了解一下@property 以及 @synthesize之间的区别和联系,说到@property 以及 @synthesize,我们就不得不提到iOS中 成员变量和属性 之间的区别和联系了。
接触iOS的人都知道,@property
声明的属性默认会生成一个_类型的成员变量,同时也会生成setter/getter
方法。 但这只是在iOS5之后,苹果推出的一个新机制。看老代码时,经常看到一个大括号里面定义了成员变量,同时用了@property声明,而且还在@implementation中使用@synthesize
方法,就像下面的代码这样:
@interface ViewController ()
{
// 1.声明成员变量
NSString *myString;
}
//2.在用@property
@property(nonatomic, copy) NSString *myString;
@end @implementation ViewController
//3.最后在@implementation中用synthesize生成set方法
@synthesize myString;
@end
其实,发生这种状况根本原因是苹果将默认编译器从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
方法。
此外,如果我们再最新的代码中声明一个成员变量,如下代码所示,那么我们只是声明了一个成员变量,并没有setter/getter
方法。所以访问成员变量时,可以直接访问name
,也可以像C++一样用self->name
来访问,但绝对不能用self.name
来访问。
@interface MyViewController :UIViewController
{
NSString *name;
}
@end
从Xcode4.4以后,即iOS的@property已经独揽了@synthesize的功能主要有三个作用:
- 生成了成员变量get/set方法的声明
- 生成了私有的带下划线的的成员变量因此子类不可以直接访问,但是可以通过get/set方法访问。那么如果想让定义的成员变量让子类直接访问那么只能在.h文件中定义成员变量了,因为它默认是@protected
- 生成了get/set方法的实现
值得注意的是:
- 如果已经手动实现了get和set方法(两个都实现)的话Xcode不会再自动生成带有下划线的私有成员变量了
- 因为xCode自动生成成员变量的目的就是为了根据成员变量而生成get/set方法的,但是如果get和set方法缺一个的话都会生成带下划线的变量
2 self.xx与_xx
上面我们说到了属性与成员变量、@property 以及 @synthesize之间的联系与区别。同时,我们提到了self.xx和_xx的一点区别,其中self.xx是调用的xx属性的get/set方法,而_xx则只是使用成员变量_xx,并不会调用get/set方法。两者的更深层次的区别在于,通过存取方法访问比直接访问多做了一些其他的事情(例如内存管理,复制值等),例如如果属性在@property中属性的修饰符有retain,那么当使用self.xx的时候相应的属性的引用计数器由于生成了setter方法而进行加1操作,此时的retaincount为2。
- 扩展:很多人觉得OC中的点语法比较奇怪,实际是OC设计人员有意为之。
点表达式(.)
看起来与C语言中的结构体访问以及java语言汇总的对象访问有点类似,如果点表达式出现在等号=
左边,调用该属性名称的setter
方法。如果点表达式出现在=
右边,调用该属性名称的getter
方法。- OC中
点表达式(.)
其实就是调用对象的setter
和getter
方法的一种快捷方式,self.myString = @"张三";
实际就是[self setmyString:@"张三"];
最后说一下容易出现的问题的地方,根据我个人的经验,最容易出问题的地方就是对属性xx或成员变量_xx的初始化的地方和调用时机,直接通过例子来看,我们将属性和实例变量的初始化放在重写的get方法中,于是我们在 - (void)viewDidLoad 中使用_invoiceInfoImageView来进行布局时,实际上因为在这之前也没有调用invoiceInfoImageView的get方法,所以此时invoiceInfoImageView的值其实为nil,界面上是空白的。
#import "InvoiceTitleInfoViewController.h" @interface InvoiceTitleInfoViewController () //定义属性invoiceInfoImageView
@property (strong, nonatomic) UIImageView *invoiceInfoImageView; @end @implementation InvoiceTitleInfoViewController - (void)viewDidLoad {
[super viewDidLoad];
self.title = @"发票抬头"; //用_xx来调用实例变量_invoiceInfoImageView,此时由于没有调用get方法进行初始化,因此此时_invoiceInfoImageView的值为nil
[self.view addSubview:_invoiceInfoImageView];
WEAKSELF
[_invoiceInfoImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(weakSelf.view).mas_offset(0.0f);
make.left.mas_equalTo(weakSelf.view).mas_offset(15.0f);
make.right.bottom.mas_equalTo(weakSelf.view).mas_offset(-15.0f);
}];
} //初始化属性invoiceInfoImageView,其实这就是invoiceInfoImageView的get方法
- (UIImageView *)invoiceInfoImageView{
if (!_invoiceInfoImageView) {
_invoiceInfoImageView = [[UIImageView alloc] init];
_invoiceInfoImageView.image = [UIImage imageNamed:@"invoice_title_info"];
}
return _invoiceInfoImageView;
}
如果我们在 使用self.xx来调用变量,则会调用invoiceInfoImageView的get方法,进行初始化,界面布局将会显示我们想要的图片。此外,如果我们再使用_xx之前用self.xx调用过变量invoiceInfoImageView,则同样会调用其get方法从而触发invoiceInfoImageView的初始化,这样也不会影响布局。
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"发票抬头"; //用self.xx来调用invoiceInfoImageView
[self.view addSubview:self.invoiceInfoImageView];
WEAKSELF
[self.invoiceInfoImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(weakSelf.view).mas_offset(0.0f);
make.left.mas_equalTo(weakSelf.view).mas_offset(15.0f);
make.right.bottom.mas_equalTo(weakSelf.view).mas_offset(-15.0f);
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"发票抬头"; //先用self.invoiceInfoImageView触发get方法进行初始化,这样_invoiceInfoImageView的值被初始化后不为nil
self.invoiceInfoImageView;
[self.view addSubview:_invoiceInfoImageView];
WEAKSELF
[_invoiceInfoImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(weakSelf.view).mas_offset(0.0f);
make.left.mas_equalTo(weakSelf.view).mas_offset(15.0f);
make.right.bottom.mas_equalTo(weakSelf.view).mas_offset(-15.0f);
}];
}
还有一点值得注意的就是我们前面提到过的,如果我们同时手动重写了一个属性的get和set方法的话,Xcode不会再自动生成带有下划线的私有成员变量了。如下图所示,在我们只定义了get方法时一切都没有问题,但是一旦我们又重写set方法,会发现用到_xx的地方就会报错。
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan
iOS学习——属性引用self.xx与_xx的区别的更多相关文章
- iOS的属性声明:retain和strong的区别
声明属性时用strong或者retain效果是一样的(貌似更多开发者更倾向于用strong).不过在声明Block时,使用strong和retain会有截然不同的效果.strong会等于copy,而r ...
- IOS学习5——属性与成员变量
[转]iOS中属性与成员变量的区别 ios中属性修饰符的作用 1. 属性用property声明 2. 简而言之,对于目前的ios开发,属性和成员变量的区别,完全可以不管. 3. 这个是历史原因造成的. ...
- iOS学习笔记-精华整理
iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...
- iOS学习笔记总结整理
来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...
- iOS学习笔记之ARC内存管理
iOS学习笔记之ARC内存管理 写在前面 ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式. 指针变量与对象所有权 指针变量暗含了对其 ...
- 【IOS学习基础】NSObject.h学习
一.<NSObject>协议和代理模式 1.在NSObject.h头文件中,我们可以看到 // NSObject类是默认遵守<NSObject>协议的 @interface N ...
- iOS学习——iOS项目Project 和 Targets配置详解
最近开始学习完整iOS项目的开发流程和思路,在实际的项目开发过程中,我们通常需要对项目代码和资料进行版本控制和管理,一般比较常用的SVN或者Github进行代码版本控制和项目管理.我们iOS项目的开发 ...
- ios 学习路线总结
学习方法 面对有难度的功能,不要忙着拒绝,而是挑战一下,学习更多知识. 尽量独立解决问题,而不是在遇到问题的第一想法是找人. 多学习别人开源的第三方库,能够开源的库一定有值得学习的地方,多去看别的大神 ...
- iOS学习笔记06—Category和Extension
iOS学习笔记06—Category和Extension 一.概述 类别是一种为现有的类添加新方法的方式. 利用Objective-C的动态运行时分配机制,Category提供了一种比继承(inher ...
随机推荐
- 基于 HTML5 Canvas 的 3D 压力器反序列化
在实际应用中,我觉得能够通过操作 JSON 文件来操作 3D 上的场景变化是非常方便的一件事,尤其是在做编辑器进行拖拽图元并且在图元上产生的一系列变化的时候,都能将数据很直观地反应给我们,这边我们简单 ...
- 如何配置 Health Check?- 每天5分钟玩转 Docker 容器技术(107)
容器状态是 UP 的,应用就是健康的吗? 还真不一定!Docker 只能从容器启动进程的返回代码判断其状态,而对于容器内部应用的运行情况基本没有了解. 执行 docker run 命令时,通常会根据 ...
- 鼠标悬停,图片放大 CSS实现
因为最近做的项目刚好用到了这个实现,分享出来 class=enlarge 为div标签的class div img 为标签 .enlarge div img:hover{ transform: s ...
- C++11新特性应用--介绍几个新增的便利算法(不更改容器中元素顺序的算法)
总所周知.C++ STL中有个头文件,名为algorithm.即算法的意思. The header<algorithm>defines a collection of functions ...
- javascript跳跃式前进(3) - 跳入JSON
前言 JSON崛起不是意外,是顺应时代;相当简洁小巧的书写模式及阅读方式; 基础 看这篇文章: JSON知识点汇总_W3SCHOOL 初步进阶 早期的解析仅仅实用eval() ,可是这货太easy给注 ...
- iOS_4_表情排列
终于效果图: BeyondViewController.h // // BeyondViewController.h // 04_表情排列 // // Created by beyond on 14- ...
- orale 查询每年、每月、每日统计量的sql语句
每年 select to_char(createtime, 'YYYY') 年, count(*) from table group by to_char(createtime, 'YYYY'); ...
- Apriori算法的C++实现
Apriori是经典的购物篮分析算法.该算法用SQL实现难度较大,所以考虑用C++实现. 花了两天,代码例如以下.原创转载请注明出处 //Apriori.c #include<iostream& ...
- Spring基础知识之依赖注入
Spring框架的四大原则: 1)使用POJO进行轻量级和最小侵入式的开发. 2)通过依赖注入和基于接口编程实现松耦合. 3)通过AOP和默认习惯进行声明式编程. 4)使用AOP和模板(templat ...
- 自学Zabbix3.5.7-监控项item-Applications
Applications应用程序用于对逻辑组中的项目进行分组.例如我们要监控MySQL,我们可以将所有和MySQL相关的item放到这个应用程序中.例如MySQL的availability of My ...