iOS基础·属性的修饰词与setter的关联(@property、@synthesize、@dynamic、retain、assign、copy、weak、strong、nonatomic、atomic、readonly、readwrite等修饰词与setter、getter等存取方法之间的涉及)

广大人讲属性修饰词的时候,喜欢从字面或者定义的角度介绍它们间的区分。这篇文章,我们强调从修饰词对setter方法的影响一贯显示区别。

1. 实例变量:命名区别于全局变量和局部变量


1.1 命名法则:

  • 以下划线_作为实例变量名字的前缀,如_student
  • 如此那般,可以很容易地经过下划线区分实例变量与任何变量(全局变量,局部变量,静态变量等)

1.2 阐明地点:

  • 在.h头文件中
  • 抑或,在.m兑现公文的类拓展

1.3 注解模式:

  • 头文件,写在接近@interface Person : NSObject {...}这么的花括号{...}里面
  • 心想事成文件的类举行中,写在近似@interface Person ()<UIScrollViewDelegate> {...}诸如此类的花括号{...}里面

1.4 讲明示例:

@interface TYPagerController ()<UIScrollViewDelegate> {
    NSInteger   _countOfControllers;
    BOOL        _needLayoutContentView;
    CGFloat     _preOffsetX;
    float    _heightInMeters;

    struct {
        unsigned int transitionFromIndexToIndex :1;
        unsigned int transitionFromIndexToIndexProgress :1;
    }_delegateFlags;

    struct {
        unsigned int transitionFromIndexToIndex :1;
        unsigned int transitionFromIndexToIndexProgress :1;
    }_methodFlags;
}

1.5 setter、getter方法

可以自己手动为实例变量在头文件
中声明setter、getter方法,并在实现文件中贯彻setter、getter方法。你也足以不注明不落实,但不用再企图调用setter、getter方法了,甚至点语法。

//一个例子
//实现getter
- (float)heightInMeters{
    return _heightInMeters;
}
//实现setter
- (float)setHeightInMeters:(float)h{
    _heightInMeters = h;
}

1.6 调用setter、getter方法

一经您兑现了setter,getter方法,才能够调用存取方法,例如:

//调用getter
float temp = [self heightInMeters];
//调用setter
[self setHeightInMeters:10.0];

1.7 点语法

即便您兑现了setter,getter方法,才可以使用点语法
简化调用存取方法

//调用getter
float temp = self.heightInMeters;
//调用setter
self.heightInMeters = 10.0;

1.8 实例变量类型:

  • 1)可以是简约的C类型,如 int _sudentNum;float _heightInMeters;

这种实例变量及其值会在注明对象的内部保存。

  • 2)可以是指针,用来指向其余对象,如NSString *tempStrCMPersonModel *personModel等等。这种属性叫目的实例变量

这种变量,注脚的对象内部仅保留指向相应实例对象的指针(对象地址),而不保留实例对象自我。实例对象自我由堆负责保存,管理机制由ARC负责。

1.9 继承特性:

  • 子类继承不了父类写在类拓展 中的示例变量

2. 属性:自动讲明实例变量和存取方法,并促成存取方法


2.1 阐明地方:

  • 阐明头文件
  • 仍旧实现公文的类举行中

2.2 阐明情势:

  • 写在@interface与@end之间,花括号{...}之外
  • 非得有@property修饰词修饰,后边可选用性地添加任何修饰词如(nonatomic,
    strong) 等

2.3 注脚示例:

#import <UIKit/UIKit.h>
@interface TMAddCategoryViewController ()
@property (nonatomic, strong) NSMutableArray *dataSource;
@end

2.4 存取方法:编译器会自行注脚和贯彻

  • @property会让编译器自动声明相应的实例变量和存取方法,并实现存取方法。除非您用别样重要词修饰,专门报告编译器做哪些其他特殊处理。
  • 尽管自动生成存取方法,碰到有的要求时,你也足以再自己重写存取方法。一般添加数据模型示例对象的时候,喜欢重写getter方法,设置有些默认值,这种叫懒加载。
  • 有一些例外,不会自动生成存取方法:
  1. 还要重写了getter setter
  2. 重写只读属性的 getter
  3. 使用了@dynamic
  4. @protocol 中定义的习性
  5. category 中定义的特性
  6. 重载的性能:当你在子类中重载父类的性质,你不可以不用 @synthesize
    手动合成

2.5 示例:重写getter

- (NSMutableArray *)dataSource
{
    if (!_dataSource) {
        _dataSource = [NSMutableArray array];
        RLMResults *results = [[TMDataBaseManager defaultManager] queryAddCategorysWithPaymentType:self.paymentType];
        for (TMAddCategory *category in results) {
            [_dataSource addObject:category];
        }
    }
    return _dataSource;
}

注:RLMResults是第三方数据库框架Realm中的一个类名

2.6 示例:重写setter

- (void)setDataSource:(id<iCarouselDataSource>)dataSource
{
    if (_dataSource != dataSource)
    {
        _dataSource = dataSource;
        if (_dataSource)
        {
            [self reloadData];
        }
    }
}

2.7 重写setter和getter导致的专门状态:

@property讲明的属性,编译器是否会师成存取方法和成员变量有如下两种专门状态

  • 若手动实现了setter方法,编译器就只会自动生成getter方法
  • 若手动实现了getter方法,编译器就只会自动生成setter方法
  • 若同时手动实现了setter和getter方法,编译器就不会自动生成不设有的分子变量

2.8 编译器自动实现的存取方法有什么毛病?

  • @property只会生成最简单易行的getter/setter方法,而不会举行数量判断

2.9 指定所生成的方法的方法名称

  • getter=你定制的getter方法名称
  • setter=你定义的setter方法名称(注意setter方法必须要有 🙂
  • 示例:

@property(getter=isMarried)BOOL married;

平凡BOOL类型的特性的getter方法要以is开端

2.10 连续特性:

  • 父类声明在头文件
    中的属性,子类不能持续这么些属性注脚的实例变量,只可以看看属性自动生成的存取方法。

3. 修饰词:@synthesize 与 @dynamic

修饰词:告诉编译器是否或咋样自动给属性生成存取方法


@property有三个照应的修饰词,一个是@synthesize,一个是@dynamic。假如@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
显明,这多少个修饰的效能是排斥的。

3.1 @synthesize 与 @dynamic

3.1.1 位置

@dynamic或者@synthesize,写在.m文件的@implementation中。

3.1.2 效能分别

知道这五个修饰词的职能,可以先看看这几个单词的意思。

  • synthesize 与 dynamic 英文意思
    • synthesize [‘sɪnθəsaɪz] v. 合成;综合
    • dynamic [daɪ’næmɪk] adj. 动态的;引力的;重力学的;有生气的
3.1.3 单词分别

单词混淆:
多线程的概念里面有个举足轻重词@synchronized跟@synthesize容易视觉混淆,这里也看看多少个单词的意趣。

  • synthesize 与 synchronized 单词相比
    • synthesize [‘sɪnθəsaɪz] v. 合成;综合
    • synchronized [‘sɪŋkrənaɪzd] adj. 协办的;同步化的 v.
      使和谐(synchronize的过去分词);同时暴发;校准

3.2 @synthesize

3.2.1 介绍

概念属性后,编译器会自行编写访问这一个属性所需的办法,此过程叫做自动合成
(autosynthesis)。需要强调的是,这些进程由编译器在编译期执行,所以编辑器里看不到那几个“合成方法”(synthesized
method)的源代码。除了生成方法代码 getter、setter
之外,编译器还要自动向类中添加适量类型的实例变量,并且在属性名后边加下划线,以此作为实例变量的名字。

3.2.2 用法
  • 两个属性可以经过一行@synthesize搞定,四个特性之间用逗号连接
  • 可以在类的兑现代码里通过@synthesize语法来指定实例变量的名字。

@synthesize name = realName;

对于位置的实例变量则为扭转的是realName而不是name,方法也应和改变。定义的实例变量是依照@synthesize name = realName;来定的。用的可比多的气象是:

@synthesize name = _name;

上述代码的情趣是,在@property
阐明的name,在setter/getter主意中应用NSObject * _name;以此实例变量来赋值与重返。

3.2.3 二种写法相比较
@synthesize age = _age;
  • setter和getter实现中会访问成员变量_age
  • 假定成员变量_age不存在,就会自动生成一个@private的分子变量_age

@synthesize age;//等效下面
@synthesize age = age;
  • setter和getter实现中会访问@synthesize后同名成员变量age
  • 若果成员变量age不存在,就会自动生成一个@private的分子变量age
3.2.4 用法场景

当你在子类中重载了父类中的属性,你不可以不利用@synthesize来手动合成实例变量。

3.3 @dynamic

3.3.1 介绍
  • @dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的习性只需提供getter即可)。
3.3.2 崩溃
  • 一旦一个属性被声称为@dynamic
    var,然后您没有提供@setter方法和@getter方法,编译的时候没问题,不过当程序运行到instance.var
    = someVar,由于缺setter方法会招致程序崩溃;或者当运行到 someVar =
    var时,由于缺getter方法同样会促成崩溃。
  • 编译时没问题,运行时才实施相应的法门,那就是所谓的动态绑定

3.4. 专注的事

  • @synthesize 仅仅是一个 clang 的 Objective-C 语言扩展(autosynthesis of properties), 然后clang恰好是 Xcode 的默认编译器.
  • 比方编译器换成了 gcc, 那么这么些特性也就没有了。

4. 其余修饰词


诸六个人讲那一个修饰词的时候,喜欢从字面或者定义的角度介绍它们间的区别。这篇作品,我们从修饰词对setter方法的震慑一贯展现区别。

retain、assign、copy、weak、strong、nonatomic、atomic、readonly、readwrite

假使为了修饰一个性质nameStr,代码如下:

#import <UIKit/UIKit.h>
@interface CMViewController ()
@property (nonatomic, strong) NSString *nameStr;
@end

里头,当括号内的修饰词(nonatomic,
strong)换成下边各个修饰词的时候,分别分析一下setter方法(有些修饰词修饰字符串并不合适,但此间仅为分析区别)。

4.1 assign

4.1.1 基本特征
  • assign (默认):直接赋值,不转移引用计数。
4.1.2 对setter的影响
  • assign修饰词对setter的熏陶:

- (void) setName:(NSString *)newValue{
  nameStr = newValue;
}
4.1.3 使用意况
  • 常用来着力数据类型(NSInteger)和C数据类型(int、float、double、char以及id类型。
  • 以此修饰符不会牵涉到内存管理,不过假如是目的类型,可能会导致内存泄漏或者EXC_BAD_ACCESS错误。
  • 除开assign以外的别样修饰符,是必须用于修饰OC对象的。
4.1.4 危险现象
  • assign修饰的目的销毁后不会把该对象的指针置nil。对象已经被销毁,但指针还在痴痴的针对它,这就成了野指针,这是相比危急的。所以assign修饰的OC属性是可怜危急的,比如,一些老的第三方框架用assign修饰的delegate属性平日会造成崩溃。

4.2 retain

4.2.1 基本特点
  • retain: 指针拷贝。指针拷贝后,地址不变,内容不变,引用计数器加1。
4.2.2 对setter的影响
  • retain修饰词对setter的熏陶:

- (void) setName:(NSString *)newValue{
  if (nameStr !=newValue){
     [nameStr release]
     nameStr = [newValue retain];
  }
}
4.2.3 使用意况
  • 适用NSObject和其子类。MRC下,用于修饰多数的OC类型的对象。ARC下,一般意义被strong取代。
  • 不能够用于焦点的数据类型或者Core
    Foundation的对象(retain操作会使对象的引用计数器加1,可是基本的数据类型或者Core
    Foundation对象压根就一直不引用计数器,所以不可以举行retain操作。换言之:基本数据类型或者CF不是指针,不是指针就不可以开展retain操作。对象即指针嘛)。

4.3 copy

4.3.1 基本特点
  • copy是内容拷贝。释放旧目的,然后建立一个索引计数为1的靶子。
  • strong修饰的属性在赋值时不会调用copy,而copy修饰的性能在赋值相当于机关多调用了两回copy方法。
4.3.2 对setter的影响
  • copy修饰词对setter的影响:

- (void) setName:(NSString *)newValue{
  if (nameStr !=newValue){
     [nameStr release]
     nameStr = [newValue copy];
  }
}
4.3.3 使用情状

copy的拔取境况为,实现了NSCopying
protocol的类,我想博得它的值,不过我又不想在原对象上改动,于是深赋值一份新的值给你,让你来随便操作。

  • 用于修饰block。
  • 用以含有可深拷贝的mutable子类的类,如NSString,NSArray,NSSet,NSDictionary,NSData的,NSCharacterSet,NSIndexSet。
  • 可是NSMutableArray这样的不得以,Mutable的无法用copy,不然起头化会有题目。

4.4 weak

weak、strong是ARC出现后才出现的定义,但这并不意味weak、strong这六个修饰都无法在MRC格局下行使。事实上,strong在MRC情势如故可应用。

4.4.1 基本特征
  • weak 用来修饰强引用的性能,类似于对应原来的assign。
  • weak是一种弱引用,并不会使对象的引用计数加1,可以避免循环引用的问题。
  • 不保留传入的靶子。假如该目的被放出,那么相应的实例变量会被自动赋为nil,不会变成悬空指针(也称野指针)。悬空指针指向的是不再存在的对象,向悬空指针发送新闻平时会造成程序崩溃。
4.4.2 二种格局下
  • MRC模式
    • weak: MRC格局下不可能采用
  • ARC模式
    • weak: 弱引用,不会使对象的引用计数器加1。
4.4.3 与assign的区别
  • weak修饰的对象销毁的时候,指针会活动安装为nil。而assign不会。
  • assign可以用来非OC对象,而weak必须用于OC对象。
4.4.4 使用情形
  • 用于修饰UI控件。
  • UI控件拖到xib/Storyboard后,系统活动为控件赋了strong,所以拖到代码就用weak就行了。
  • 代理属性。@property (nonatomic, weak) id delegate; // 修饰代理属性
4.5.5 对setter的影响
  • weak修饰词对setter的熏陶:倘使nameStr和newValue都是用weak修饰的特性

[nameStr release]
nameStr = newValue;

4.5 strong

4.5.1 基本特性

strong 用来修饰强引用的习性,类似于对应原来的retain。

4.5.2 三种模式
  • MRC模式
    • strong: 与retain等价
  • ARC模式
    • strong: 强引用(它使对象的引用计数加1)
4.5.3 使用境况
  • 当要保住某个对象的命,让这么些目标足以用来其它的情势时(即在某段时间内要时不时用到那么些目的,又不想每回用到这些目的都要重新alloc),此时您要把这么些目的变成强指针,即成为strong,让strong强引用着那个目的,使那一个目标不会被放飞。
  • 用以修饰NSMutableArray,NSMutableDictionary等copy不可能修饰的特性。
  • 一般的指针变量默认就是strong类型的,所以我们对于strong变量不加__strong修饰。

NSString *name = self.nameField.text;  // 等价
__strong NSString *name = self.nameField.text;  // 等价
4.5.4 对setter的影响
  • strong修饰词对setter的震慑:倘诺nameStr和newValue都是用strong修饰的属性

[newValue retain]
[nameStr release]
nameStr = newValue;

4.6 读写属性

读写性修饰符——readwrite、readonly

4.6.1 readwrite
  • readwrite(默认): 可读可写(系统自动创制getter 和 setter 方法)
4.6.1 readonly
  • readonly: 只读,系统只会生成 getter方法

4.7 原子属性

4.7.1 atomic
  • 1.原子属性,阐明的性质默认就是atomic.所以底层默认为性能的setter方法加锁,目的就是严防多(条)线程访问同一个内存地址,造成数据失实。
  • 2.在多线程环境下,原子操作特别有必要,因为它能提供多线程安全,假使没有原子操作,可能引起特别。—>线程爱慕
  • 3.亟需耗费大量的资源。
4.7.2 nonatomic
  • 1.非原子属性,不会为setter方法加锁。
  • 2.并未关联多线程的编程时,用nonatomic。
  • 3.不会耗费大量的资源,所以会提升性能。

5. @property的多少个例子

@property (nonatomic, readonly) CGFloat itemWidth;
@property (nonatomic, assign) double brightness;
@property (nonatomic, assign, getter=isOpenMenu) BOOL openMenu;
@property (nonatomic, strong) UILabel *newsBooksLabel;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, weak) IBOutlet UIButton *nextButton;
@property (nonatomic, copy) void (^cancelBtnBlock)();
@property (nonatomic, weak) id<TMSideCellDelegate> sideCellDelegate;

6. 推介阅读

相关文章