美文网首页
iOS--@property关键字

iOS--@property关键字

作者: mayuee | 来源:发表于2020-07-10 17:32 被阅读0次

    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

    相关文章

      网友评论

          本文标题:iOS--@property关键字

          本文链接:https://www.haomeiwen.com/subject/bfbscktx.html