OC语言学习记录

目录

一、主要的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的词典也分为可变词典和不可变词典

二、IOS属性

1、什么是属性

属性是OC语言的一个机制,我们在OC中用@property来声明的一个属性,其实@property是一种语法糖,编译器会自动为你的实例变量生成getter方法和setter方法。

2、属性修饰符

A、读写性修饰符

(1)readwrite

表明这个属性是可读可写,是系统默认属性,系统为我们创建settergetter方法。

(2)readonly

表明这个属性只可读不能写,系统只为我们创建getter方法,不会创建setter方法。

B、原子性修饰符

(1)atomic

原子属性,线程安全但可能降低性能。

(2)nonatomic

非原子属性,提高性能但非线程安全。

C、assgin和weak

weak与assgin都表示了一种非持有关系,也不被称为弱引用,在调用时,不会增加引用对象的引用计数。weak在引用对象被销毁时,会被指向nil;而assgin不会被置为nil。

(1)assign

如果用assgin修饰对象,当assgin指向的对象被销毁时,assgin就会指向一块无效内存(变成悬空指针),如果这个时候你给assgin发送消息,程序就会发生崩溃,也有可能不会崩溃,这个取决于你发送消息时,那块内存还是否存在。

assgin一般修饰的基础数据类型(NSInteger,int,float,double,char,bool等),因为基础数据类型是被分配到栈上,栈的内存会由系统自动处理,所以不会造成悬空指针。

(2)weak

只用于修饰对象。

D、strong和copy

使用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的区别

E、retain

retain修饰NSArray,NSDate。对应的setter方法。

与assign相对,我们要解决对象被其他对象引用后释放造成的问题,就要用retain来声明。retain声明后的对象会更改引用计数,那么每次被引用,引用计数都会+1,释放后就会-1,即使这个对象本身释放了,只要还有对象在引用它,就会持有,不会造成什么问题,只有当引用计数为0时,就被dealloc析构函数回收内存了。

IOS属性

IOS深拷贝和浅拷贝

三、iOS内存管理

1、什么是内存管理?

内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。

2、内存是怎么分配的,是分配在哪里的?

在IOS中数据是存在堆和栈中的,然而我们的内存管理是管理堆上的内存,栈上的内存并不需要我们管理(计算机会自动回收)。

  • 基础数据类型(非OC对象)存储在栈上
  • OC对象存储在堆上
int a = 10; //栈
int b = 20; //栈
Car *c = [[Car alloc] init]; 

在内存中的表现形式如下:

OC语言学习记录_第1张图片

3、什么是引用计数

引用计数是一个简单而有效的管理对象生命周期的方式。当我们创建一个新的对象的时候,它的引用计数为1,当有一个新的指针指向这个对象时,我们将其引用计数+1,当某个指针不在指向这个对象,我们将其引用计数-1,当对象的引用计数为0是,说明该对象不再被任何指针指向了,我们就可以将对象销毁,回收内存。如图所示。

OC语言学习记录_第2张图片

 4、MRC手动管理引用计数

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 或者其它未知行为

5、ARC自动管理引用计数

ARC其实也是基于引用计数,只是编译器在编译时期自动在已有代码中插入合适的内存管理代码(包括retain,release,copy,autorelease,autoreleasepool)以及在Runtime做一些优化。

ARC已经处理了大部分的引用计数,还有一小部分的引用计数需要自己管理,这一部分就是底层Core Foundation对象因为不在ARC的管理下,所以需要自己维护这些对象的引用计数。

所有权修饰符:

OC编程中为了处理对象,可将变量类型定义为id类型或各种对象类型。ARC中id类型和对象类型其类型必须附加所有权修饰符。

以下是四种所有权修饰符:

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

所有权修饰符和属性修饰符对应关系如下表:

所有权修饰符 属性修饰符
__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对象,需要自己手工管理他们的引用计数时,显得一筹莫展。

循环引用问题:

引用计数这种内存管理方式虽然很简单,但是有一个比较大的瑕疵,就是他不能很好的解决循环引用问题。如下图所示:

OC语言学习记录_第3张图片

对象A和对象B,相互引用了对方作为自己的成员变量,只有当自己销毁时,才会将成员变量的引用计数-1。因为对象A的销毁依赖于对象B销毁,而对象B的销毁也依赖于对象A的销毁,这样就造成了我们称之为循环引用的问题,这两个对象即使在外界已经灭有任何指针能够访问到他们了,他们也无法被释放。

不止存在两对象循环引用的问题,多个对象依次持有对方,形成一个环状,也可以造成循环引用问题,而且在真实的编程环境中,环越大就越难被发现。如下图所示:

OC语言学习记录_第4张图片

解决循环引用问题,主要有两种方式:

  • 主动断开循环引用

我明确知道这里存在循环引用,在合理的位置主动断开环中的一个引用,使得对象得以回收

  • 使用弱引用

弱引用虽然持有对象,但是并不增加引用计数,这样就避免了循环引用的产生。在IOS开发中,弱引用通常在delegate模式中使用。举个例子来说,两个ViewController A和B,ViewController A需要弹出一个ViewController B,让用户输入一些内容,当用户输入完成后,ViewController B需要将内容返回给ViewController A。这个时候,ViewController的delegate成员变量通常是一个弱引用,以避免这两个ViewController相互引用对方造成循环引用问题,如下图所示。

OC语言学习记录_第5张图片

弱引用的实现原理就是这样,系统对于每一个弱引用的对象,都维护一个表来记录他所有的弱引用的指针地址。当一个对象的引用计数为0,系统就通过这张表,找到所有的弱引用指针,继而把他们都置成nil。

7、Core Foundation 对象的内存管理

底层的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内存管理

你可能感兴趣的:(ios,objective-c)