原子性
- 原子性就nonatomic和atomic两个关键字
atomic与nonatomic
-
atomic是默认的原子操作关键字,atomic也就代表其具有原子性
-
atomic 属性关键字会给该 property 的 getter和setter方法加锁,但不能保证线程一定安全并且会带来更多损耗。
-
而nonatomic就是非原子性的,不会给setter,getter方法加锁,这也是我们默认的方法
-
原因:
- 使用atomic也无法保证线程一定能安全了
- 对于涉及到多线程安全的场景,我们常常是自己去额外加同步
- 这样也可以加快运行速度
为什么会出现线程不安全
- 这部分主要要看哲神的博客:从atomic关键字说到多线程安全(内含iOS给代码加锁方法总结)
- 补充下地址总线的知识:地址总线 (Address Bus;又称:位址总线) 属于一种电脑总线 (一部份),是由CPU 或有DMA 能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/地方的实体位址。数据总线的宽度,随可寻址的内存组件大小而变,决定有多少的内存可以被访问。
- atomic所说的线程安全只是保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的。
- 这里推荐的哲神的文章其中对于地址总线方面的看法还是一个猜想,到底怎么样也是没人知道
读写权限
- 读写权限这一块很好理解,就是一个readwrite以及一个readonly
- readwrite:其修饰的属性拥有“获取方法”(getter)与“设置方法”(setter)。
- readonly:其修饰的属性仅拥有获取方法。
- 这一块可以联想到runtime源码中,关于一个对象结构体中要有rw以及ro结构体,他们其实就是readwrite以及readonly的区别
内存管理
- 终于到重头戏了,其实对于上面这两个部分,敲过一段时间的iOS开发者心里都会有数,像原子性,无脑nonatomic,像读写权限,一般直接默认不写,真正有难度,比较复杂的都在内存管理部分
- 下面先把最重要的四个:assgin,strong,weak,copy讲一下
内存管理四天王——ASWC
assgin
- assgin用于基本数据类型的默认属性关键字,它等于就是啥也没干,不会使得引用计数加一,赋值的时候就是简单的赋值,简单来说,就是用于不需要非对象
- 这里要讲清楚一个概念,在探究内存管理属性关键字的时候,会大量涉及到引用计数+1的概念,这里要注意,都指的是赋值的时候会不会引用计数加一。
- 什么意思呢?之前有是看了看组里大一的同学学习,印象很深的有一个同学写了个NSInteger *obj = xxxx
- 这样的写法可能是来自于认为以NS开头的都是OC对象,可惜的是,如果查看过NSInteger的定义,就这么一句:typedef long NSInteger;
- 所以,我们讲到的基本数据类型其实就是非对象的数据类型,学到现在,你看到已经知道,指针是存在栈区,对象本身是存在堆区,两者本身不是连在一起的
- 而指针里面会存着对象的地址,也就是指针指向了对象
- 霹雳吧啦讲了一大堆,我们拉回来看下assgin的作用,由于是直接赋值,不改变引用计数,这就限制了我们只能用它来来修饰非对象
- 假如我们使用它来修饰一个对象,首先会报警告【Assigning retained object to unsafe property; object will be released after assignment】;我们假设有A,B两个指针指向某个对象,在有引用计数管理的情况下,其引用计数不为0就不会释放,而如果我们用的是assgin,就会导致该对象直接被释放了。A,B指针没有被置nil,出现野指针
- 而用它来修饰基本数据类型,由于它们都是被分配在栈上的,栈的内存由系统总结自动处理回收
strong
- strong是对象的默认内存管理关键字,在学过ARC中的strong修饰符后,基本就不会有什么问题了
- 是对象的默认属性关键字,此特质表明该属性定义了一种“持有关系”,为这种属性设置新值时,设置方法会先保留(retain)新值,并释放(release)旧值,然后再将新值设置上去。
- 这个感觉真没啥好说的,看学长面试讲到strong都很尬
weak
- 此特质表明该属性定义了一种“非持有关系”。为这种属性设置新值时,设置方法既不保留(retain)新值,也不释放(release)旧值。此特质同assign类似,然而在属性对象所指的对象遭到摧毁(dealloc)时,属性值也会清空(置nil)。
- weak要深挖就参看我runtime系列文章中相关部分就行
copy
- 终于到重头戏copy了,我们好好研究下
- 此特质表达的所属关系与strong类似。然而设置方法并不保留新值,而是将其拷贝(copy)
- 这一部分推荐看哲神的详解iOS开发中复制对象
- NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
- 理解:这里我认为意思就是由于NSString存在可变类型NSMutableString,那么假设我们整了一个NSString类型的属性A,又有一个NSMutableString类型的可变字符串B,我们把B赋给A;假如我们的A使用strong的话,他就是直接指针赋值,A,B指向同一块内存,这样我们修改B,同时就会修改A;而当我们使用的是copy的时候,这样我们A持有就是一个B的不可变副本,那么不管B怎么改,A都是不变的
杂鱼小虾
- retain:在ARC时代,strong等于retain,引用计数会加一
- unsafe_unretained:此特质的语义和assign相同,但它适用于“对象类型”,该特质表达一种“非持有关系”,当目标对象销毁时,属性值不会自动清空(unsafe),这一点与weak有区别。
总结
默认关键字
- 基本数据:atomic,readwrite,assign
- 普通的 OC 对象: atomic,readwrite,strong
weak与assgin的区别
- weak 策略在属性所指的对象遭到摧毁时,系统会将 weak 修饰的属性对象的指针指向 nil,在 OC 给 nil 发消息是不会有什么问题的;如果使用 assign 策略在属性所指的对象遭到摧毁时,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,很容造成程序奔溃
- assigin 可以用于修饰非 OC 对象,而 weak 必须用于 OC 对象。
查漏补缺:
- strong、weak、retain、assgin、copy、unsafe_unretained
retain:释放旧对象,提高输入对象的引用计数+1,将输入对象的值赋值于旧对象,只能用户声明OC对象
@property (nonatomic, retain) Room *room;
- (void)setRoom:(Room *)room // room = r
{
// 只有房间不同才需用release和retain
if (_room != room) {
// 将以前的房间释放掉 -1,将旧对象释放
[_room release];
// MRC中需要手动对房间的引用计数器+1
[room retain];
_room = room;
}
}
strong:强引用,它是ARC特有
。在MRC时代没有,相当于retain。由于MRC时代是靠引用计数器来管理对象什么时候被销毁所以用retain,而ARC时代管理对象的销毁是有系统自动判断,判断的依据就是该对象是否有强引用对象。如果对象没有被任何地方强引用就会被销毁。所以在ARC时代基本都用的strong来声明代替了retain。只能用于声明OC对象(ARC特有)
苹果官网对strong的解释代码:
Precondition:object is a valid pointer to a __strong object which is adequately aligned for a pointer. value is null or a pointer to a valid object.
Performs the complete sequence for assigning to a __strong object of non-block type [[*]]. Equivalent to the following code:
void objc_storeStrong(id *object, id value) {
id oldValue = *object;
value = [value retain];
*object = value;
[oldValue release];
}
assgin:简单的赋值操作,不会更改引用计数,用于基本的数据类型声明。
weak:弱引用,表示该属性是一种“非拥有关系”。为这种属性设置新值时既不会保留新值也不会释放旧值,类似于assgin。 然而在对象被摧毁时,属性也会被清空(nil out)。这样可以有效的防止崩溃(因为OC中给没有对象地址的指针发送消息不会崩溃,而给有内存地址但地址中是空对象的指针发消息会崩溃,野指针),该声明必须作用于OC对象。对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。(ARC特有),strong 和 weak的指针,根本区别在于,strong执行了retain操作,而weak没有。
苹果官网对weak的说明
id objc_storeWeak(id *object, id value);
Precondition: object is a valid pointer which either contains a null pointer or has been registered as a __weak >object. value is null or a pointer to a valid object.If value is a null pointer or the object to which it points has begun deallocation, object is assigned null and >unregistered as a __weak object. Otherwise, object is registered as a __weak object or has its registration >updated to point to value.
Returns the value of object after the call.
copy:不同于其他声明,copy会根据声明的属性是否是可变类型而进行不同操作。如果对象是一个不可变对象,例如NSArray NSString 等,那么copy等同于retain、strong。如果对象是一个可变对象,例如:NSMutableArray,NSMutableString等,它会在内存中重新开辟了一个新的内存空间,用来 存储新的对象,和原来的对象是两个不同的地址,引用计数分别为1. 这就是所谓的深拷贝浅拷贝,浅拷贝只是copy了对象的内存地址,而深拷贝是重新在内存中开辟新空间,新空间对象值与拷贝对象的值一样。但是是完全不同的2个内存地址。 例如copy修饰的类型为 NSString不可变对象时,copy可以保护其封装性,当赋值对象是一个 NSMutableString 类时(NSMutableString是 NSString 的子类,表示一种可修改其值的字符串),此时若是不用copy修饰拷贝字符串,那么赋值该对象之后,赋值对象字符串的值就可能会在其他地方被修改,修改后赋值后对象也会改变,造成值不对。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
unsafe_unretained:和weak 差不多,唯一的区别便是,对象即使被销毁,指针也不会自动置空, 对象被销毁后指针指向的是一个无用的内存地址(野地址)。如果对象销毁后后还使用此指针,程序会抛出 BAD_ACCESS 的异常。 所以一般不使用unsafe_unretained。目前我还未在实际项目中使用过该声明。(ARC特有)
- 2.4 readOnly、readWrite、getter=、setter=
readOnly表示属性仅能读不能设置其值。告诉编译器只生成getter方法不生成setter方法。
readWrite默认值,表示属性可读可写。编译器会自动生成getter setter方法
getter=指定属性gettter方法的方法名
@property (nonatomic, strong, getter=getMyDic) NSMutableDictionary *dic;
setter=指定属性setter方法的方法名
@property (nonatomic, strong,setter=myArray:) NSMutableArray *arr;
2.5__unsafe_unretained、__weak、__strong
__unsafe_unretained
NSMutableArray __unsafe_unretained *array = [[NSMutableArray alloc]init];
[array addObject:@"123"];
使用__unsafe_unretained修饰符的变量与使用__weak修饰符的变量一样,因为自己生成并持有的对象不能继续为自己持有,所以生成的对象会立即被释放。也就是说在执行完init方法以后,对象指针所指向的内存就已经释放掉了,但因为用的__unsafe_unretained修饰指针并没不像__weak的指针那样,将指针自动置为nil,它依然指向原来的地址,可是这块地址的内存已经被系统回收了,再访问就是非法的,也就是野指针,再执行后面的addObject方法自然会出错了。
__weak
主要用于解决循环引用,用__weak修饰的变量 当对象释放后,指针自动设置为nil,当后面继续使用该指针变量的时候不会造成crash,更不会造成强引用使该释放的对象无法释放,造成内存泄露。
__weak typeof(self) weakSelf = self;
__strong
相反与__weak,主要用于当使用某个对象是,希望它没有提前被释放。强引用该对象使其无法释放。例如在block内部,希望block调用时该对象不会被提前释放造成错误。可以使用强引用。
TestAlertView *alertView = [TestAlertView new];
alertView = ^()
{
//当block内部需要使用本身这个局部对象时,需要用强引用方式,让alertView在传递完block后不会被释放依然可以执行setTitle操作
__strong typeof(alertView) strongAlertView = alertView;
[strongAlertView setTitle:@"1234"];
}
[alertView show];
网友评论