在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的功能主要有三个作用:

  1. 生成了成员变量get/set方法的声明
  2. 生成了私有的带下划线的的成员变量因此子类不可以直接访问,但是可以通过get/set方法访问。那么如果想让定义的成员变量让子类直接访问那么只能在.h文件中定义成员变量了,因为它默认是@protected
  3. 生成了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中点表达式(.)其实就是调用对象的settergetter方法的一种快捷方式,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的区别的更多相关文章

  1. iOS的属性声明:retain和strong的区别

    声明属性时用strong或者retain效果是一样的(貌似更多开发者更倾向于用strong).不过在声明Block时,使用strong和retain会有截然不同的效果.strong会等于copy,而r ...

  2. IOS学习5——属性与成员变量

    [转]iOS中属性与成员变量的区别 ios中属性修饰符的作用 1. 属性用property声明 2. 简而言之,对于目前的ios开发,属性和成员变量的区别,完全可以不管. 3. 这个是历史原因造成的. ...

  3. iOS学习笔记-精华整理

    iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...

  4. iOS学习笔记总结整理

    来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...

  5. iOS学习笔记之ARC内存管理

    iOS学习笔记之ARC内存管理 写在前面 ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式. 指针变量与对象所有权 指针变量暗含了对其 ...

  6. 【IOS学习基础】NSObject.h学习

    一.<NSObject>协议和代理模式 1.在NSObject.h头文件中,我们可以看到 // NSObject类是默认遵守<NSObject>协议的 @interface N ...

  7. iOS学习——iOS项目Project 和 Targets配置详解

    最近开始学习完整iOS项目的开发流程和思路,在实际的项目开发过程中,我们通常需要对项目代码和资料进行版本控制和管理,一般比较常用的SVN或者Github进行代码版本控制和项目管理.我们iOS项目的开发 ...

  8. ios 学习路线总结

    学习方法 面对有难度的功能,不要忙着拒绝,而是挑战一下,学习更多知识. 尽量独立解决问题,而不是在遇到问题的第一想法是找人. 多学习别人开源的第三方库,能够开源的库一定有值得学习的地方,多去看别的大神 ...

  9. iOS学习笔记06—Category和Extension

    iOS学习笔记06—Category和Extension 一.概述 类别是一种为现有的类添加新方法的方式. 利用Objective-C的动态运行时分配机制,Category提供了一种比继承(inher ...

随机推荐

  1. 免费靠谱的 Let’s Encrypt 免费 https 证书申请全过程

    申请 Let’s Encrypt证书的原因: 现在阿里云等都有免费的 https 证书,为什么还要申请这个呢(估计也是因为阿里云这些有免费证书的原因,所以 Let’s Encrypt 知道的人其实并不 ...

  2. Material04 MdListModule模块

    1 在共享模块中导入MdListModule模块 import { NgModule } from '@angular/core'; import { CommonModule } from '@an ...

  3. iOS日历中给一个事件加入多个提醒

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 假设认为写的不好请多提意见,假设认为不错请多多支持点赞.谢谢! hopy ;) iOS自带的日历应用中,我们最多仅仅能给一个事件设置2个提醒, ...

  4. 算法题:A除以B

    题目描写叙述 本题要求计算A/B.当中A是不超过1000位的正整数,B是1位正整数.你须要输出商数Q和余数R,使得A = B * Q + R成立. 输入描写叙述: 输入在1行中依次给出A和B,中间以1 ...

  5. SourceTree 基本介绍

    Git的服务器端: 最出名的是GitHub,但是不能创建私有仓库,创建私有得需要Money Bitbucket:可以创建私有数据库,但是速度太慢,太消磨激情了 如果既想创建私有又想要激情,那只能自己搭 ...

  6. Linux小记 — Ubuntu自动化配置

    前言 工欲善其事,必先利其器.经过多次的重复配置ubuntu开发坏境,我终于决定花点时间总结一下,并将其写成一个自动化配置脚本.服务器实例:ubuntu 16.04,技术栈:shell,python. ...

  7. springMVC学习总结(三)数据绑定

    springMVC学习总结(三)数据绑定 一.springMVC的数据绑定,常用绑定类型有: 1.servlet三大域对象: HttpServletRequest HttpServletRespons ...

  8. Intellij IDEA中使用Protobuf的正确姿势

    一..proto文件语法高亮显示 需要安装Protobuf Support插件 依次点击Intellij中的"File"-->"Settings"--&g ...

  9. 轨迹系列——Socket总结及实现基于TCP或UDP的809协议方法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在上一篇博客中我详细介绍了809协议的内容.809协议规范了通 ...

  10. Swift3.0 创建工程常用的类、三方、以及扩展 1.5

    搭建项目常用的方法属性,欢迎追加 三方: source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_framew ...