iOS--@property关键字

2020-07-10  本文已影响0人  mayuee

iOS中主要的属性修饰符有下面几种:

读写权限:readonly, readwrite

安全策略:nonatomic,atomic (决定编译器生成的getter和setter是否为原子操作)

内存管理:copy / assign / retain,strong / weak /  unsafe_unretained

方法名 :getter,setter

不常用:nonnull, null_resettable,nullable

按照MRC和ARC区分

MRC:copy / assign / retain / readwrite / readonly /nonatomic / atomic 等

ARC:copy / assign / strong / weak / readwrite / readonly /nonatomic / atomic 等

在声明 property 时,如果不指定关键字,编译器会为 property 生成默认的关键字

基本数据类型,默认关键字为:atomic,assign,readwrite

对象类型,默认关键字为       :atomic,strong,readwrite

下面介绍一下这些关键字

1、strong   (默认)

强引用。用于OC对象,为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再把新值设置过去,属性引用对象的引用计数 +1。

2、weak

对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。

1>delegate使用weak修饰:因为代理一般都是指向控制器,会造成循环引用,无法释放,造成内存泄露。比如tableview,代码中经常会有:

//控制器强引用self.view,self.view强引用tableView

[self.view addSubview:self.tableView];

//delegate 引用控制器,如果delegate用strong修饰会导致循环引用,无法释放,造成内存泄漏

self.tableView.delegate = self;

self.tableView.dataSource = self;

2>UI中控件经常使用weak的原因:

UI控件之所以可以使用弱指针,是因为控制器有强指针指向UIView

UIView 有强指针指向Subviews数组,数组中由强指针指向控件

一般来说,类 “内部”的属性设置为strong,类“外部”的属性设置为weak。说到底就是一个归属权的问题。使用weak通常为了解决循环引用的问题

3、assign

默认属性关键字。 assign用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。

assign也能修饰OC对象,但是assign修饰的对象在被释放后,其指针依然存在,不会被置为nil——出现了野指针。当访问这个野指针时,指向了原地址,而原地址有两种情况:

第一种情况:原地址没有改变,代码运行通过,但很有可能有逻辑bug。

第二种情况:原地址已经改变,结果不可预测,多数crash,也有可能出现其他莫名错误。

但是用weak来修饰的话,对象释放的时候会把指针置为nil,从而避免了野指针的出现。

为什么基本数据类型就可以使用assign,这涉及到堆和栈的问题,基本数据类型会被分配到栈空间,而栈空间是由系统自动管理分配和释放的,就不会造成野指针的问题。

4、copy 拷贝  

拷贝。返回一个不可变对象。源对象是不可变对象是浅拷贝,类似 strong;当源对象是可变对象时执行的是单层深拷贝。对自定义对象使用copy时,自定义对象需要遵守NSCopying协议并实现copyWithZone方法。。详细见前面文章iOS--拷贝相关题 - 简书

使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值变化会无意间篡改不可变类型对象原来的值。

5、retain

适用于NSObject及其子类。release旧值,retain新值。

retain是指针复制(浅复制),引用计数加1,而不会导致内容被复制。

retain是MRC下的关键字,ARC下使用strong代替。

6、readwrite(默认)

读写属性。@synthesize自动生成setter和getter方法,可以通过点语法读取和设置属性的值。

7、readonly

只读属性。@synthesize只会自动合成getter方法。如果使用点语法为属性赋值,会编译错误:Assignment to readonly property。

8、getter=

指定获取属性的方法名

@property(nonatomic, getter=getName, copy)NSString *str;

self.str = @"getter=getName";

NSLog(@"%@",self.getName);

2020-07-10 14:39:30.160040+0800 iOS_property[91440:5164076] getter=getName

9、setter=

指定设置方法的方法名。

@property(nonatomic, setter=setName:, copy)NSString *str;

    [self setName:@"setter=setName:"];

    NSLog(@"%@",self.str);

2020-07-10 14:42:28.162017+0800 iOS_property[91536:5166626] setter=setName:

10、unsafe_unretained

用于修饰对象类型,声明一个弱引用,和用assign修饰对象类型一样,当所修饰的对象销毁时,该属性不会被置为nil。

11、atomic(默认)

原子的。提供多线程安全,在多线程环境下,原子操作是必要的,atomic会给对象加锁(对象的setter和getter函数里)假设多个线程访问同一属性,所有不同线程上的操作都将依次顺序执行。atomic读写线程安全,但不是绝对安全,atomic只是给getter和setter加了个锁,只能保证代码进入getter或者setter函数内部时是安全的,一旦出了getter和setter,atomic无法保障多线程安全了。还有如果修饰的是集合类型属性,对集合属性的读写是安全的,但集合内添加移除元素就不保证安全了。

因为iOS中使用同步锁的开销比较大,会影响性能,所以有特殊要求安全性高的才使用atomic,一般属性声明都是nonatomic。

12、nonatomic

非原子性,指出访问器不是原子操作。在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定 nonatomic ,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值,对属性赋值的时候不加锁,多线程并发访问会提高性能。但是如有多线程同时访问同一个非原子的属性,可能会出现无法预料的结果,如果没有使用多线程间的通讯编程,nonatomic是一个非常好的选择。

13、nonnull

对象不能为nil;当赋值为nil时编译器会给出警告。

NS_ASSUME_NONNULL_BEGIN   //之间的属性都被认为是nonnull的NS_ASSUME_NONNULL_END

14、nullable

对象可以为nil;属性默认是nullable的。

————————————————————————————————————————

部分面试题

1、关于weak 与assign

weak:弱引用, ARC后出现,只可以修饰对象(delegate和block等引用类型),就是指针类型,不会改变引用对象的计数;不能修饰基本数据类型,否则会报错:Property with 'weak' attribute must be of object type。weak引用的对象释放后,指针会被自动置为 nil,不会出现野指针,也避免循环引用。

assign:通常用于修饰基本数据类型。如果修饰对象,不会改变引用对象的计数,引用的对象释放后指针不会自动被置空而产生野指针问题,此时向对象发消息会崩溃。

2、关于copy与strong

NSString、NSArray、NSDictionary常用copy,为什么不用strong?

见文章iOS--拷贝相关题 - 简书

3、weak如何实现自动赋nil

Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个哈希表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)的数组因为一个对象可能被多个弱引用指针指向,所以这里Value是一个数组。

当这个对象引用计数为 0 时就会dealloc。假如 weak 指向的对象内存地址是address,那么就会以address为Key, 在这个 weak 表中搜索,找到以address为Key的指向这个对象的weak 指针的数组,遍历数组将weak指针设置为 nil。

weak实现原理可概括四步:

一、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

二、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

三、对象释放。

1. objc_release

2. 因为引用计数为0所以执行dealloc

3. _objc_rootDealloc

4. object_dispose

5. objc_destructInstance

6. objc_clear_deallocating

四、对象释放时,调用objc_clear_deallocating函数。

1、objc_clear_deallocating函数首先根据对象地址获取所有weak指针的数组;

2、然后遍历这个数组把其中的weak指针设为nil;

3、把这个记录从weak表中删除;

4、从引用计数表中清理废弃对象的地址作为键值的记录

https://opensource.apple.com/source/objc4/objc4-646/runtime/objc-weak.h

上一篇下一篇

猜你喜欢

热点阅读