目录
一、主要的OC的基本类型介绍
二、IOS属性
1、什么是属性
2、属性修饰符
A、读写性修饰符
B、原子性修饰符
C、assgin和weak
D、strong和copy
E、retain
三、iOS内存管理
1、什么是内存管理?
2、内存是怎么分配的,是分配在哪里的?
3、什么是引用计数
4、MRC手动管理引用计数
5、ARC自动管理引用计数
7、Core Foundation 对象的内存管理
四、参考文档
本文档是作者自学的总结文档,如有侵权,请联系作者,作者进行删除文档
OC的数据类型有:
字符串,浮点型,整型,布尔类型 而且OC的数组也分为可变数组和不可变数组 OC的词典也分为可变词典和不可变词典
属性是OC语言的一个机制,我们在OC中用@property
来声明的一个属性,其实@property
是一种语法糖,编译器会自动为你的实例变量生成getter方法和setter方法。
(1)readwrite
表明这个属性是可读可写,是系统默认属性,系统为我们创建setter
和getter
方法。
(2)readonly
表明这个属性只可读不能写,系统只为我们创建getter
方法,不会创建setter
方法。
(1)atomic
原子属性,线程安全但可能降低性能。
(2)nonatomic
非原子属性,提高性能但非线程安全。
weak与assgin都表示了一种非持有关系,也不被称为弱引用,在调用时,不会增加引用对象的引用计数。weak在引用对象被销毁时,会被指向nil;而assgin不会被置为nil。
(1)assign
如果用assgin修饰对象,当assgin指向的对象被销毁时,assgin就会指向一块无效内存(变成悬空指针),如果这个时候你给assgin发送消息,程序就会发生崩溃,也有可能不会崩溃,这个取决于你发送消息时,那块内存还是否存在。
assgin一般修饰的基础数据类型(NSInteger,int,float,double,char,bool等),因为基础数据类型是被分配到栈上,栈的内存会由系统自动处理,所以不会造成悬空指针。
(2)weak
只用于修饰对象。
使用strong和copy是都会使引用对象引用计数+1。但是使用copy修饰的属性在某些情况下赋值的时候会创建对象的副本,就是深拷贝。strong是两个指针指向同一个内存地址,copy是在内存中拷贝一份对象,两个指针指向不同的内存地址。
(1)copy
copy修饰NSString、NSArray、block属性。我们以为copy只是深拷贝,其实不是的,系统在当源字符串为不可变类型时,你的属性copy其实就是进行浅拷贝,当源字符串为可变类型时,才进行深拷贝。但是我们建议在使用NSString属性的时候用copy,避免可变字符额修改导致一些非预期问题。
注:
1、对不可变对象执行copy操作,是浅拷贝(指针拷贝,内容相同),都指向同一边存储空间,源数据被修改,副本数据也会被修改
2、对不可变字符串执行mutCopy操作,是深拷贝(对象拷贝),两者指向不同的存储空间
3、可变字符串执行copy或mutCopy都是深拷贝
4、可变容器类执行copy或mutcopy或者不可变容器执行mutCopy都是不完全深拷贝,即只是容器对象指向不同的内存空间,内部的元素则指向同一个内存
5、可变数组执行copy(NSMutableArray执行copy后返回的NSArray),在使用过程回出现crash的问题
6、数组完全深拷贝需要执行initWithArray:copyItems: 方法 NSArray *deepCopy = [[NSArray alloc] initWithArray:array copyItems:YES];
(2)strong
strong修饰NSString、block以外的OC对象。
注:
strong与copy的区别:
(1)当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。
(2)当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。
所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。
但是,我们一般声明NSString时,也不希望它改变,所以一般情况下,建议使用copy,这样可以避免NSMutableString带来的错误。
strong与copy的区别
retain修饰NSArray,NSDate。对应的setter方法。
与assign相对,我们要解决对象被其他对象引用后释放造成的问题,就要用retain来声明。retain声明后的对象会更改引用计数,那么每次被引用,引用计数都会+1,释放后就会-1,即使这个对象本身释放了,只要还有对象在引用它,就会持有,不会造成什么问题,只有当引用计数为0时,就被dealloc析构函数回收内存了。
IOS属性
IOS深拷贝和浅拷贝
内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。
在IOS中数据是存在堆和栈中的,然而我们的内存管理是管理堆上的内存,栈上的内存并不需要我们管理(计算机会自动回收)。
int a = 10; //栈
int b = 20; //栈
Car *c = [[Car alloc] init];
在内存中的表现形式如下:
引用计数是一个简单而有效的管理对象生命周期的方式。当我们创建一个新的对象的时候,它的引用计数为1,当有一个新的指针指向这个对象时,我们将其引用计数+1,当某个指针不在指向这个对象,我们将其引用计数-1,当对象的引用计数为0是,说明该对象不再被任何指针指向了,我们就可以将对象销毁,回收内存。如图所示。
a、在MRC中增加引用计数都是需要自己手动释放的。
对象操作 | OC中对应的方法 | 引用计数的变化 |
---|---|---|
生成并持有对象 | alloc/new/copy/mutableCopy等 | +1 |
持有对象 | retain | +1 |
释放对象 | release | -1 |
废弃对象 | dealloc | 0 |
b、四则法则
/*
* 自己生成并持有该对象
*/
id obj0 = [[NSObeject alloc] init];
id obj1 = [NSObeject new];
/*
* 持有非自己生成的对象
*/
id obj = [NSArray array]; // 非自己生成的对象,且该对象存在,但自己不持有
[obj retain]; // 自己持有对象
/*
* 不在需要自己持有的对象的时候,释放
*/
id obj = [[NSObeject alloc] init]; // 此时持有对象
[obj release]; // 释放对象
/*
* 指向对象的指针仍就被保留在obj这个变量中
* 但对象已经释放,不可访问
*/
/*
* 非自己持有的对象无法释放
*/
id obj = [NSArray array]; // 非自己生成的对象,且该对象存在,但自己不持有
[obj release]; // ~~~此时将运行时crash 或编译器报error~~~ 非 ARC 下,调用该方法会导致编译器报 issues。此操作的行为是未定义的,可能会导致运行时 crash 或者其它未知行为
ARC其实也是基于引用计数,只是编译器在编译时期自动在已有代码中插入合适的内存管理代码(包括retain,release,copy,autorelease,autoreleasepool)以及在Runtime做一些优化。
ARC已经处理了大部分的引用计数,还有一小部分的引用计数需要自己管理,这一部分就是底层Core Foundation对象因为不在ARC的管理下,所以需要自己维护这些对象的引用计数。
所有权修饰符:
OC编程中为了处理对象,可将变量类型定义为id类型或各种对象类型。ARC中id类型和对象类型其类型必须附加所有权修饰符。
以下是四种所有权修饰符:
所有权修饰符和属性修饰符对应关系如下表:
所有权修饰符 | 属性修饰符 |
---|---|
__unsafe_unretained | assign |
__unsafe_unretained | unsafe_unretained |
__strong | copy |
__strong | strong |
__strong | retain |
__weak | weak |
a、__strong简介
__strong表示强引用,对应定义property时用到的是strong。当对象没有任何一个强引用指向他的时候,他才会被释放。如果声明引用时不加修饰符,那么引用将默认是强引用。当需要释放强引用指向的对象时,需要保证所有指向对象强引用置为nil。_strong修饰符是id类型和对象类型默认的所有权修饰符。
b、__weak简介
__weak表示弱引用,对应定义property时用的是weak。弱引用不会影响对象的释放,而当对象被释放时,所有指向他的弱引用都会自定被置为nil,这样可以防止野指针。使用__weak修饰的变量,即是使用注册到autoreleasepool(自动释放池)中的对象。__weak最常见的一个作用就是用来避免循环引用。
c、__unsafe_unretained简介
这个修饰符在定义property是对应的是unsafe_unretained。__unsafe_unretained修饰的指针纯粹只是指向对象,没有任何额外的操作,不会去持有对象使得对象retainCount+1。而在指向的对象被释放时依旧原原本本的指向原来的对象地址,不会被自动置为nil,所以会变成野指针。
d、__autoreleasing简介
将对象赋值给附有__autoreleasing修饰符的变量等同于MRC时调用对象的autorelease方法。 、6、ARC下的内存管理问题
a、过度使用block之后,无法解决循环引用问题。
b、遇到底层Core Foundation对象,需要自己手工管理他们的引用计数时,显得一筹莫展。
循环引用问题:
引用计数这种内存管理方式虽然很简单,但是有一个比较大的瑕疵,就是他不能很好的解决循环引用问题。如下图所示:
对象A和对象B,相互引用了对方作为自己的成员变量,只有当自己销毁时,才会将成员变量的引用计数-1。因为对象A的销毁依赖于对象B销毁,而对象B的销毁也依赖于对象A的销毁,这样就造成了我们称之为循环引用的问题,这两个对象即使在外界已经灭有任何指针能够访问到他们了,他们也无法被释放。
不止存在两对象循环引用的问题,多个对象依次持有对方,形成一个环状,也可以造成循环引用问题,而且在真实的编程环境中,环越大就越难被发现。如下图所示:
解决循环引用问题,主要有两种方式:
我明确知道这里存在循环引用,在合理的位置主动断开环中的一个引用,使得对象得以回收
弱引用虽然持有对象,但是并不增加引用计数,这样就避免了循环引用的产生。在IOS开发中,弱引用通常在delegate模式中使用。举个例子来说,两个ViewController A和B,ViewController A需要弹出一个ViewController B,让用户输入一些内容,当用户输入完成后,ViewController B需要将内容返回给ViewController A。这个时候,ViewController的delegate成员变量通常是一个弱引用,以避免这两个ViewController相互引用对方造成循环引用问题,如下图所示。
弱引用的实现原理就是这样,系统对于每一个弱引用的对象,都维护一个表来记录他所有的弱引用的指针地址。当一个对象的引用计数为0,系统就通过这张表,找到所有的弱引用指针,继而把他们都置成nil。
底层的Core Foundation对象,在创建时大多以XxxCreateWithXxx这样的方式创建,例如:
// 创建一个 CFStringRef 对象
CFStringRef str= CFStringCreateWithCString(kCFAllocatorDefault, “hello world", kCFStringEncodingUTF8);
// 创建一个 CTFontRef 对象
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);
对于这些对象的引用计数的修改,要相应的使用 CFRetain 和 CFRelease 方法。如下所示:
// 创建一个 CTFontRef 对象
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);
// 引用计数加 1
CFRetain(fontRef);
// 引用计数减 1
CFRelease(fontRef);
对于CFRetain和CFRelease两个方法,可以与OC对象的retain和release方法等价。所以对于底层Core Foundation对象,我们用手工管理引用计数的办法就可以。
但是在ARC下,我们有时需要将一个Core Foundation对象转换成OC对象,需要引入bridge相关的关键字。 _bridge:只做类型转换,不修改相关对象的引用计数,原来的Core Foundation对象在不用时,需要调用CFRelease方法。 _bridge_retained:类型转换后,将相关对象的引用计数+1,原来的Core Foundation对象在不用时,需要调用CFRelease方法。 _bridge_transfer:类型转换后,将该对象的引用计数交给ARC管理,Core Foundation对象在不用时,不再需要调用CFRelease方法。
IOS内存管理
strong与copy的区别
IOS属性
IOS深拷贝和浅拷贝
IOS内存管理