OC内存管理
参考官方文档 关于内存管理
Objective-C有三种内存管理方式:
MRR(manual retain-release):通过跟踪你所拥有的对象来显式地管理内存,采用了”引用计数( reference counting)”的模型。该模型由基础类
NSObject和运行时Runtime共同提供ARC(Automatic Reference Counting):系统采用MRR相同的引用计数系统,不同的是,在编译的时候插入了内存管理的方法。
GC(Garbage Collection):Mac下才能使用,iOS不支持
内存管理存在的两种错误:
释放(free)或者覆盖(over-write)正在使用中的数据。 这会造成内存异常,导致应用程序崩溃,数据损坏。
不再使用的内存没有被释放,导致内存泄漏。
内存泄露,就是有内存分配但是不释放它,哪怕这块内存已经不用了。泄露,导致你的应用程序占用越来越多的内存,并导致整体性能的下降,或者在 iOS 平台上导致应用终止。
NSObject定义了一个析构方法dealloc,当一个对象被清除时,这个方法会被自动调用。
引用计数
每个对象都有一个引用计数
- 当新建一个对象时,它的 retain count 为 1
- 发送 retain 消息给一个对象时,它的 retain count 加 1
- 发送 release 消息给一个对象时,它的 retain count 减 1
- 发送 autorelease 消息,它的 retain count 将在未来某个时候减 1
- 如果 retain count 是 0,就会被 dealloc
自动引用计数(ARC)
ARC是iOS 5推出的功能,它不需要在代码中进行retain、release操作,但会自动执行引用计数,即引用计数的保留或释放是编译器自动隐性添加的。也就是说,原来用来处理内存管理的引用计数代码是手动编写的,现在编译器在编译之前都会自动隐性地添加它,然后才开始进行编译。
使用ARC时有两个基本原则:
不得手动调用
retain、release、autorelease和dealloc方法。只能由编译器生成调用代码。dealloc方法可以在子类中重写,但是不能被调用,即不可以在dealloc方法中调用父类的dealloc方法
需要注意,ARC是编译器特性,不是垃圾回收机制,它和java的垃圾回收是完全不同的,不要把ARC机制当成是垃圾回收。垃圾回收机制是运行时的特性,当发现在运行过程中,有不需要的对象就清理回收;而ARC机制是编译器的特性,就是在编译的时候,哪里需要内存管理代码,就自动在哪里插入引用计数代码。
强指针与弱指针
OC中指向对象的指针分为强指针和弱指针两种,也称为强引用或弱引用。
强指针
ARC机制能自动地完成对象的引用计数操作,是因为ARC机制遵守一定的引用规则。苹果官方对于ARC机制的引用规则是,只要没有强指针指向的对象,对象就会被自动销毁。也就是说对象在内存中的存在是因为有强指针的引用。强指针变量使用关键字“__strong”修饰,默认情况下,所有的指针都是强指针,因此无需显式的声明。
弱指针
弱引用不增加引用计数。默认情况下,引用 OC 对象的指针都是强指针,所以想要定义弱指针变量必须使用关键字“__weak”修饰。
__weak Person * p = [[Person alloc]init];
对弱指针对象发送消息时,需要注意,当你发送消息给一个被 dealloc 的弱指针对象时,程序会崩溃。
对象 A 和对象 B,相互引用了对方作为自己的成员变量,只有当自己销毁时,才会将成员变量的引用计数减 1。因为对象 A 的销毁依赖于对象 B 销毁,而对象 B 的销毁与依赖于对象 A 的销毁,这样就造成了我们称之为循环引用(Reference Cycle)。循环引用比较容易出现在Block、Timer引用中。
Autorelease Pool
参考官方文档 使用自动释放池
当程序中产生大量的临时对象,可以考虑使用自动释放池,避免内存泄漏。
for (int i = 0; i < 1000000; i++) {
@autoreleasepool {
NSString *string = [NSString stringWithString:@"ABC"];
NSLog(@"%@",string);
}
}
浅复制与深复制
- 浅复制:是指针复制,让复制前和复制后对象的指针指向同一块内存空间。
- 深复制:是内存复制,让复制前和复制后对象的指针指向内容相同的两块内存空间
区别:
- 浅复制增加原对象的引用计数,但不会分配新的堆内存空间
- 深复制不增加原对象的引用计数,但会分配新的堆内存空间
示例:
NSString * str = @"hello";
NSString * strCopy = [myString copy];
NSMutableString * strMutCopy = [myString mutableCopy];
NSLog(@"str地址: %p",str);
NSLog(@"strCopy地址: %p",strCopy);
NSLog(@"strMutCopy地址: %p",strMutCopy);
NSMutableString *mutString = [[NSMutableString alloc]initWithString:@"hello,world"];
NSString * mutStringCopy = [mutString copy];
NSMutableString * myMutStringCopy = [mutString mutableCopy];
NSLog(@"mutString地址: %p",mutString);
NSLog(@"mutStringCopy地址: %p",mutStringCopy);
NSLog(@"myMutStringCopy地址: %p",myMutStringCopy);
小结:
不可变对象:
copy方法是浅复制,生成的对象是不可变对象;mutableCopy是深复制,生成的对象是可变对象。可变对象:
copy是深复制,生成的对象是不可变对象;mutableCopy是深复制,生成的是可变对象。
自定义对象的复制
需要遵守NSCopying协议或NSMutableCopying协议,分别实现copyWithZone:和mutableCopyWithZone:方法
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCopying, NSMutableCopying>
@property (nonatomic) int age;
@property (nonatomic, copy) NSString *name;
@end
#import "Person.h"
@implementation Person
- (id)copyWithZone:(NSZone *)zone{
// 1.创建新对象
Person *p = [[[self class] allocWithZone:zone] init];
// 2.拷贝属性值
p.age = _age;
p.name = _name;
// 3.返回新的对象
return p;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
// 1.创建新对象
Person *p = [[[self class] allocWithZone:zone] init];
// 2.拷贝属性值
p.age = _age;
p.name = _name;
// 3.返回新的对象
return p;
}
- (NSString *)description{
return [NSString stringWithFormat:@"name = %@, age = %i", _name, _age];
}
@end
int main(int argc, const char * argv[]) {
/*
* 在copyWithZone方法中创建一个副本对象, 然后将当前对象的值赋值给副本对象即可
*/
Person *p = [[Person alloc] init];
p.age = 20;
p.name = @"zhangsan";
NSLog(@"%@", p);
Person *p2 = [p copy];
Person *p3 = [p mutableCopy];
NSLog(@"%@", p2);
return 0;
}
公众号“编程之路从0到1”