一、前言
     一个object的属性允许其他object监督和改变他的状态。但是在一个设计良好的面向对象程序中,直接访问一个object的内部状态是不可能的。相反,存取器(getter setter)方法是一个抽象相互作用object的底层数据。
 
通过访问器方法与属性进行交互
@property指令的目标是通过自动的创建这些存取器方法使创建和配置属性变得更加简单。它允许你在语义级别上指定公有属性的行为。而且它比较关注你的详细实现。
     这个模型调查各种各样的属性,这些属性可以让你修改getter和setter行为。其中的一些属性确定是如何处理内存的,所以这个模型也服务于在Objective-C中对内存管理的实际的介绍。
 二、@property指令
     首先,让我们看一下当我们直接使用@property时发生了什么事情,考虑一下下面的程序,一个Car类和它的实现。
Car.h
#import <Foundation/Foundation.h>

@interface Car : NSObject

@property BOOL running;
@end Car.m #import "Car.h" @implementation Car @synthesize running = _running; //Xcode 4.4以上可选
@end

  

   编译器会为running属性创建一个getter和setter方法。默认的命名习惯是用属性自己作为getter,加上前缀set作为setter方法,并且在前面加下划线作为实例变量,就像下面这样:
-(BOOL)running
{
return _running;
}
-(void)setRunning:(BOOL)running
{
_running = running;
}
当用
@property直接生成属性,你可以直接调用这些方法,就像这些方法就是包含在该类的interface和实现文件中。你也可以在.m中重写他们,但是这样会使得@synthesize指令强制。然而,你应该很少需要传统的存取器尽管@property属性供这样做在抽象的级别。
属性访问可以是用类实例后加.访问。所以看下面的代码:

Car *honda = [[Car alloc] init];
honda.running = YES;
NSLog(@"%d",honda.running);
 当执行honda.running时,也就是调用setRunning:方法。
 当给他分配值并且运行时,就是调用getter方法。
为了改变这种存取器方式,我们可以在@properry后加括号指定,下面就是介绍可用的属性。
1、The getter= and setter= Attributes     
        如果我们不喜欢@property默认的命名方式,我们可以通过The getter= and setter= Attributes 来改变存取器方法名。最常用的就是对Boolean属性使用这个。可以把getter把惯例的改成is,例如:

 @property(getter=isRunning) BOOL running;
     现在生成存储器叫做isRunning和setRunning.而标注公共性质还是叫做running。下面是我们应该怎么用逗号使用它。

 Car *honda = [[Car alloc] init];
honda.running = YES;
NSLog(@"%d",honda.running);
NSLog(@"%d",[honda isRunning]);
    这些是唯一的属性,他们都是boolean标记。
2、readonly属性
     readonly属性是一个很方便的方法让你的属性只读。这样会省略setter方法,并且防止作恶通过.调用,但是getter不受影响。例如,我们修改running的属性为readonly,注:我们可以制定多个属性,然后用“,”分开:
#import <Foundation/Foundation.h>
@interface Car : NSObject @property(getter=isRunning,readonly) BOOL running; -(void)startEngine;
-(void)stopEngine;
@end

  

不是让其他object改变running的属性,我们将会设置两个方法去访问。这两个方法的而实现如下:
-(void)startEngine
{
_running = YES;
}
-(void)stopEngine
{
_running = NO;
}
要记得,@property还为我们生成了一个实例变量,这就是我们为什么可以访问_running在没有声明的条件下(我们也可以直接使用self.running因为这个属性是只读的)。让我们来运行下列代码测试:
   Car *honda = [[Car alloc] init];
// honda.running = YES;
NSLog(@"%d",honda.running);
honda.running = NO;
我们会发现最后一句出错,因为它是只读属性,无法修改。
到这个地方,我们可以很方便快捷地让我们避免书写样板的getter和setter方法。而对remaining属性,这不是一个好的情况。他们也只适用于属性存储OC对象(相当于C数据类型)。
3、nonatomic属性  
      原子性(Atomicity)的作用是属性在线程的环境中怎么行为。当你不仅仅有一个线程, 那么getter和setter可能会在同一时间去调用,这就意味着getter/setter可能会被另一个方法打扰,很有可能造成数据错误。
                 原子的属性会封锁object,防止这种情况发生,确保get或者set操作的操作对象是完整的。没有被损坏。然而,这仅仅是一个线程安全的方面,我们必须要理解这一点。使用原子性并不能确保我们的代码就是线程安全的。
                  属性用@property声明默认是原子性的,这会导致一些花销,因此,如果你不是处在多线程环境(或者你正实现你自己的线程安全),你会用notatomic属性重写这个行为,就像下边:
   @property (nonatomic) NSString *model;  //设置非原子性
     当使用原子性属性时,会有一个小的而且比较实际的警告。针对原子属性的属性访问器必须要么是生成的,要么是用户自定义的,只有传统非原子性的属性会让你混合搭配合成存储方法。你可以看一下,如果移去nonatomic从上边的代码,然后再Car.m添加传统的getter。
         会产生一个警告:Setter和getter必须被合成,或者用户自定义,或者属性必须是nonatomic
4、内存管理
      在面向对象语言中,objects存在于计算机内存中,尤其在移动设备中,内存这是一个很缺乏的资源。内存管理系统以一个高效的管理方式创建和破坏object,目标就是确保程序不占用超出它所需要的空间。
          许多语言都是通过垃圾回收去完成的,但是OC用的是一个更加高效替代品,就是Object ownership(目标拥有者)。当你开始与对象交互时,你会告诉对象自己,这就意味着它确保只要你再使用对象,它就会存在.当你不再使用的时候,你放弃所有权,如果对象没有其他的所有者,操作系统会销毁这个对象,然后释放底层内存资源。
          随着自动引用计数(ARC)出现,编译器自动管理你所有的对象,大多数情况下,意味着你从来不必担心内存管理系统是怎么工作的,但是,你必须明白strong,weak和copy属性,因为他们告诉编译器对象应该有什么关系。
5、strong属性
          无论对象被指定为什么属性,强壮的属性可以创建拥有关系,这对所有对象属性来说是一种内隐行为,它默认是安全的,因为只要它被指定为strong属性,它就会确保对对象的值存在。
     通过下面的例子,让我们看一下它是怎么工作的。
@interface Person : NSObject

@property(nonatomic)NSString *name;
@end
它的实现如下,它@property产生的使用默认的存储方法,它也重写了NSObject的描述方法,返回一个代表该对象的字符串。
#import "Person.h"

@implementation Person

-(NSString *)description
{
return self.name;
}
@end
 然后,让我们添加Person属性给车,改变Car.h如下:
#import <Foundation/Foundation.h>
#import "Person.h" @interface Car : NSObject @property (nonatomic) NSString *model;
@property(nonatomic,strong) Person *driver; @end
然后考虑下边的代码: 
    Person *john = [[Person alloc] init];
john.name = @"John"; Car *honda = [[Car alloc] init];
honda.model = @"Honda Civic";
honda.driver = john;
NSLog(@"%@ is driving the %@",honda.driver,honda.model);
     只要driver是一个strong关联,honda对象就会持有john,这确保只要honda需要它,它就会有效。
6、weak属性
大多数情况下,强属性可以直观知道你想要什么对象属性,强引用会暴漏一个问题,例如,我们需要从driver引用他正在开的Car对象,     首先,我们需要给Person添加一个car属性:
Person.h
#import <Foundation/Foundation.h>

@class Car;

@interface Person : NSObject

@property(nonatomic)NSString *name;
@property(nonatomic,strong)Car *car; @end
@class Car 是对Car类的前声明,就像它告诉编译器,“相信我,Car类是存在的,所有不要想着去立刻找到它”。我们不用#import这样做,因为Car也导入了Person.h,那样我们会陷入无尽的导入循环(编译器不喜欢无穷的循环)。
然后,添加下面的代码,在honda、driver分配后:
Person *john = [[Person alloc] init];
john.name = @"John";
Car *honda = [[Car alloc] init];
honda.model = @"Honda Civic";
honda.driver = john;
john.car = honda; //添加这行
NSLog(@"%@ is driving the %@",honda.driver,honda.model);
这样我们现在有一个现象,就是john拥有honda,honda拥有john。这就意味着他们相互拥有,所以尽管他们不再有用,内存管理系统也不能够释放他们。
 
这叫做 retain cycle(保持循环),是一种内存泄露的形式,内存泄露是很不好的。幸运的是,要想解决这个办法很简单,只需要告诉其中一个属性维持一个weak属性引用另一个对象。在Person.h中,改变car的声明:
@property(nonatomic,weak)Car *car;
这种weak(弱)属性会给car创建一个非拥有关系。它允许john有一个honda的引用,同时避免了保持循环。但是,还有一个可能性就是honda会被销毁,而这个时候john正在引用honda,如果这种情况发生,weak属性会很方便地设置car为nil,去避免悬挂指针。
 
一个在Person中弱引用car
一个公用的使用weak属性例子就是父亲-孩子数据结构,根据约定,父对象应该保持一个强引用对子对象,子对象应该存储一个弱引用父对象。弱引用在代理设计模式中是内在的,也就是自带的。
     重点是两个对象不恩给你都是强引用彼此,weak属性让保持一个不通过创建保持循环循环的关系变成可能。

7、copy属性

      它是strong的替代品,不是保持拥有一个存在的对象,而是创建一个引用,无论你指定什么属性,都会持有这个拥有。只有符合NSCopying protocol的对象才能使用这个属性。
     代表值的属性(相对于链接或关系)是一个使用copy的不错选择。例如,开发者通常复制字符串属性,而不是强引用它们:
//Car.h
@property (nonatomic,copy) NSString *model;
         现在Car将会存储一个全新的实例,不管我们指定的model值,如果你处理可变的值,它会有一个好处,就是冻结对象无论什么时候有值当它被指定时。演示如下:
         Car *honda = [[Car alloc] init];
honda.model = @"Honda Civic"; NSMutableString *model = [NSMutableString stringWithString:@"Honda Civic"];
honda.model = model; NSLog(@"%@",honda.model);
[model setString:@"Nissa Versa"];
NSLog(@"%@",honda.model); //输出结果还是Honda Civic
NSMutableString 是NSString的一个子类,它可以在自身的基础上修改它的值。如果model属性不被声明为copy属性,我们将会看到在最后一个NSLog将会输出Nissa Versa
8、其他属性
上面的的这些@property性质我们需要使用在如今的OC应用中,但是下面的一些属性我们可能会在老得类库或者文档中偶尔遇到。
  • retain属性
 retain属性是手动释放保留版本的strong,它有准确一样的影响:为指定的values声称所有权.ARC环境中不能使用。
  • unsafe_unretained属性
     它类似与weak,但是如果引用对象销毁,不自动设置nil。使用该属性的唯一原因就是:去让你的类兼容不支持weak属性的代码。
  • assign属性
     当给一个这个属性指定一个新的value,assign属性不执行任何类型的内存管理调用。这个一个原始数据类型的默认行为,它的应用是在iOS5之前去实现弱引用,就像retain,现在的application你不应该明确的使用。
总结:
     这个模块讲解了所有的@property选择属性,下面是总结属性:
    1. getter=  让getter方法使用自定义的名字
    2. setter = 让setter方法使用自定义名字
    3. readonly 不合成setter方法
    4. nonatomic 不保证在多线程环境下存取访问器的完整性,这比原子性更加高效
    5. strong 在属性和指定value之间创建一个拥有关系,这是默认的对象属性
    6. weak 在属性和制定value之间创建一个不拥有关系,使用这个防止循环引用
    7. copy 创建一个指定值的副本,而不是引用存在的对象实例。
三、参考
 
四、更新 2018-04-08更新
1.这里需要注意:我们所谓的所有属性关键字都是在设置方法时才触发,也就是说现在有一个name的属性,如果使用_name,那么属性关键字不会生效,因为这些关键词都是在setter中才能起作用。举个例子:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,copy)NSArray *constArr;
@property (nonatomic,strong)NSMutableArray *mArr;
@property (nonatomic,strong)NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad]; _mArr = [[NSMutableArray alloc] initWithObjects:@"",@"", nil];
_constArr = _mArr;
NSLog(@"%@",self.constArr);
[_mArr addObject:@""];
NSLog(@"%@",self.constArr);
}
@end

这个例子中,虽然我们对constArr使用的是copy,但是两次打印的结果是不一样的。如果我们这样写:

#import "ViewController.h"
@interface ViewController ()
@property (nonatomic,copy)NSArray *constArr;
@property (nonatomic,strong)NSMutableArray *mArr;
@property (nonatomic,strong)NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad]; _mArr = [[NSMutableArray alloc] initWithObjects:@"",@"", nil];
self.constArr = _mArr;
NSLog(@"%@",self.constArr);
[_mArr addObject:@""];
NSLog(@"%@",self.constArr);
}
@end

输出打印的结果才会是一样的,因为前者只是对实例变量的指针赋值,而后者才是利用了属性特性,对属性进行赋值。这也正解释了我们平时所理解的:

对于strong,为这种属性设置新值得时候,设置方法会先保留新值,并释放旧值,然后将新值设置上去。

对于copy,设置方法并不保留新值,而是将其拷贝。

上面提到的设置方法就是我们说的setter,也就是后者使用self.constArr调用的方法。

@property括号内属性讲解的更多相关文章

  1. iOS 中@property() 括号中,可以填写的属性?

    通过@property 和 @synthesize 属性可以简化设置器(set)和访问器(get) 的代码. 在@property 中又有那些属性呢? readwrite 默认 readonly 只读 ...

  2. 第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现

    第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现 一.    案例说明 本节将通过一个案例介绍怎么使用property定义快捷的属性访问.案例中使用Rectan ...

  3. 第7.23节 Python使用property函数定义属性简化属性访问的代码实现

    第7.23节 Python使用property函数定义属性简化属性访问的代码实现 一.    背景       在本章前面章节中,我们介绍了类相关的知识,并举例进行了说明,在这些例子中会定义一些形如 ...

  4. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  5. OC中property的有关属性

    property的有关属性: (1)readwrite是可读可写特征:需要生成getter方法和setter方法: (2)readonly是只读特性只会生成getter方法不会生成setter方法: ...

  6. windowSoftInputMode属性讲解

    windowSoftInputMode属性讲解(下面这段内容我参考别人的博客,并加入我的一些意见) 我们从这个属性的名称中,可以很直观的看出它的作用,这个属性就是来设置窗口软键盘的交互模式的.andr ...

  7. 关于@property()的那些属性及ARC简介

    @property()常用的属性有:nonatomic,atomic,assign,retain,strong,weak,copy. 其中atomic和nonatomic用来决定编译器生成的gette ...

  8. 关于@property()的那些属性及ARC简介【nonatomic,atomic,assign,retain,strong,weak,copy。】

    @property()常用的属性有:nonatomic,atomic,assign,retain,strong,weak,copy. 其中atomic和nonatomic用来决定编译器生成的gette ...

  9. IOS UITableView NSIndexPath属性讲解

    IOS UITableView NSIndexPath属性讲解   查看UITableView的帮助文档我们会注意到UITableView有两个Delegate分别为:dataSource和deleg ...

随机推荐

  1. jq的ajax交互封装

    jq封装的ajax,然后 在此前和此后都是很多要考虑的  ,何不 想想构思封装下. 下面: 基本上网页都存在各种ajax,使得网页变得更加易于操作. 举个长长的例子吧: <input type= ...

  2. 以ActiveMQ为例JAVA消息中间件学习【2】

    前言 之前我们学习了什么是消息中间件,以ActiveMQ为例做了一个最简单的消息中间件的实现.但是我们做的就只能算是个例子而已,因为在实际的项目中肯定会有spring插一脚,所以spring肯定有来管 ...

  3. QMessageBox的使用

    /** 使用非静态API,属性设置API **/ QMessageBox msgBox; msgBox.setWindowTitle("Note");/** 设置标题 **/ ms ...

  4. 简明awk教程(Simple awk tutorial)

    整理翻译.原文地址:http://www.hcs.harvard.edu/~dholland/computers/awk.html 简明awk教程 为什么选awk? awk小巧.快速.简单.awk语言 ...

  5. Java提高篇之理解java的三大特性——封装

    三大特性之—封装 封装从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐 ...

  6. 在C#中,如何连接已加密的Sqlite数据库

    对数据加密分两种,一种是对数据库本身进行加密,另一种是对数据表中的数据进行加密, 如果SQLite数据库加密,我这里使用的一个管理工具叫SQLiteDeveloper,如下就可以加密数据库 , 如果在 ...

  7. PowerDesigner 修改Name ,Code 不变

    tools-> General   Options-> Dialog:Operation   Modes: 去掉 NameToCodeMirroring 前面的√      

  8. Java容器类源码分析之Iterator与ListIterator迭代器(基于JDK8)

    一.基本概念 迭代器是一个对象,也是一种设计模式,Java有两个用来实实现迭代器的接口,分别是Iterator接口和继承自Iterator的ListIterator接口.实现迭代器接口的类的对象有遍历 ...

  9. SSM+solr 通过商品搜索学习solr的简单使用

    学习了一下https://github.com/TyCoding/ssm-redis-solr这个github上的solr搜索功能,现在来记录一下. 我的理解就是solr有点类似于数据库,但它是有索引 ...

  10. Java的适配器模式

    所谓适配器模式,其实很简单,就是将一个类的接口转换成客户端所期待的另一个接口,使得原本不兼容的两个类可以一起工作. 假设我们有一个Type-c接口,但是要使用的接口确实usb的接口,这时候要怎么办呢? ...