📅  最后修改于: 2023-12-03 15:31:26.343000             🧑  作者: Mango
iOS内存管理是开发iOS应用程序时必不可少的一部分。iOS设备上的应用程序每时每刻都在使用内存,因此需要确保内存管理良好。本文将介绍iOS内存管理的基本概念,如何避免内存泄漏和如何使用ARC(自动引用计数)进行内存管理。
在了解如何进行内存管理之前,首先需要了解一些基础概念,这些概念在iOS的内存管理中是至关重要的。
堆和栈都是计算机内存中的两个基本概念。在iOS的内存管理中,堆和栈都占据着重要的位置。
堆是一块用于存储动态分配的内存的区域。通常情况下,在堆上分配的内存需手动释放,否则会导致内存泄漏。在iOS中,使用alloc
和init
方法分配内存时,所分配的内存就位于堆中。
栈是一块用于存储局部变量和函数调用信息的内存区域。栈基本上是一个先进后出的数据结构。当函数被调用时,在栈上分配了一块存储空间,当函数退出时,该存储空间即被回收。在iOS中,栈一般用于存储函数调用所使用的参数和局部变量。
引用计数是iOS内存管理中的一种技术,用于跟踪每个对象被引用的次数。当对象被创建时,引用计数为1。当对象被引用或复制时,其引用计数会增加。当对象不再被引用或使用时,其引用计数会减少。当某个对象的引用计数变成了0,这个对象就被标记为不再需要,系统会释放它所占用的内存。
在iOS中,可以手动调用一些方法来增加和减少对象的引用计数。例如,retain
方法会增加对象的引用计数,而release
方法会减少对象的引用计数。要注意的是,手动管理引用计数需要非常小心、细心,否则容易导致内存泄漏或者野指针问题。
自动引用计数(Automatic Reference Counting,ARC)是iOS5引入的一种新特性,它使用编译器技术自动管理对象的引用计数。ARC会在编译时自动插入合适的retain
和release
代码,使得开发者无需手动管理引用计数,从而避免了内存泄漏和野指针问题。
ARC的使用非常简单,只需要在Xcode中打开ARC选项,编译器就会自动为你插入相应的代码。要注意的是,虽然ARC很方便,但是适当的内存管理还是非常重要的,需要时刻关注内存使用情况,避免出现内存占用过高的情况。
内存泄漏指的是程序中存在一些无用的对象,但是这些对象的引用计数依然不为0,从而导致这些对象占用的内存无法被释放,从而引起内存占用过高的问题。内存泄漏是每一个iOS开发者都需要避免的问题。
内存泄漏可能出现在很多地方,比如未释放的对象、循环引用、过度使用单例等。下面是一些常见的内存泄漏情况以及如何避免它们。
未释放的对象是内存泄漏最常见的情况之一。当我们使用alloc
、copy
、retain
等方法创建了一个对象后,需要在不再需要该对象时手动调用release
方法释放对象占用的内存。
// 内存泄漏示例
- (void)someMethod {
NSString *str = [[NSString alloc] initWithFormat:@"%@", @"Hello World"];
// Do something with str
// 这里忘记释放对象str,导致内存泄漏
}
// 解决内存泄漏
- (void)someMethod {
NSString *str = [[NSString alloc] initWithFormat:@"%@", @"Hello World"];
// Do something with str
[str release]; // 释放对象str占用的内存
}
循环引用指的是两个或多个对象彼此持有对方的引用,导致它们的引用计数不为0,无法释放内存。循环引用最常见的情况是在 UIViewController 和 UIView 之间的循环引用。
// 循环引用示例
@interface MyViewController : UIViewController
@property (nonatomic, strong) MyView *myView;
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.myView = [[MyView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.myView];
self.myView.delegate = self;//导致了循环引用
}
@end
@interface MyView : UIView
@property (nonatomic, weak) MyViewController *delegate;
@end
@implementation MyView
@end
// 解决循环引用
@interface MyViewController : UIViewController
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
MyView *myView = [[MyView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:myView];
myView.delegate = self;
}
@end
@interface MyView : UIView
@property (nonatomic, weak) MyViewController *delegate;
@end
@implementation MyView
@end
在上面的代码中,我们将 MyViewController 类中的 myView 属性声明为弱引用(weak),这样即使 MyView 持有了 MyViewController 的引用,由于 MyViewController 引用的是 MyView,MyView 又没持有 MyViewController 的引用,两个对象的引用计数都会变为 0,从而避免了循环引用。
单例是一种经典的设计模式,它能够确保全局只有唯一一个实例对象。但是过度使用单例,可能会导致内存占用过高的问题。
如果单例创建在较早的时候,并且在开发过程中一直存在着,就会一直占用内存。因此,如果单例在不使用的时候可以释放掉,就应该尽早释放。如果是在 UIViewController 中使用的单例,那就在 dealloc 方法中释放相应的单例对象。
在使用Block的时候,需要特别注意不要捕获self,不然也会导致循环引用的问题。解决方式是增加弱引用(weak)的self。
// 使用Block
- (void)someMethod {
[myObj doSomethingWithCompletion:^(NSArray *results) {
[self processData:results];
}];
}
// 解决循环引用
- (void)someMethod {
__weak typeof(self) weakSelf = self;
[myObj doSomethingWithCompletion:^(NSArray *results) {
[weakSelf processData:results];
}];
}
ARC是自动引用计数,一种编译期间自动插入 retain 和 release 方法的技术,因此避免了手动管理引用计数的问题。
ARC支持所有的Objective-C对象,包括Core Foundation对象,不支持用于C基本类型的指针。
使用ARC之后,你就不需要主动调用retain
和release
等方法来处理对象的引用计数了,但是ARC依然需要用到autorelease pool,并且需要处理循环引用。
默认情况下Xcode创建的工程都是使用ARC的。
使用ARC后,你需要了解对象引用计数的规则是什么。当对象不会再被引用时,ARC会自动将其释放。以下是自动释放管理的几条引用计数规则:
retain
等方法。copy
方法分配一个对象的时候,其引用计数为1。由于ARC不需要开发者手动进行retain和release特别是 dealloc等释放工作,但开发者仍然需要时刻关注内存的占用情况,作出适当的内存管理,以避免内存泄漏。
以下是一些ARC开发中的内存管理技巧:
- (MyObject *)myObject {
if (!_myObject) {
_myObject = [[MyObject alloc] init];
}
return _myObject;
}
在这个例子中,只有当需要使用myObject
实例的时候,才会初始化该实例。
- (void)someMethod {
NSMutableArray *array = [[NSMutableArray alloc] init]; // 定义为局部变量
// Do something with array
// 离开作用域后,对象会自动释放
}
weak
引用来避免循环引用: weak
引用会自动将引用对象的引用计数减少1,所以当一个对象不再有强引用时,对象就会被直接释放。例如:@property (nonatomic, weak) MyClass *myObject; // 声明一个弱引用
在以上的例子中,如果 Myclass 的引用计数变成了 0,myObject 就会被自动置为 nil
。
避免使用单例: 单例通常会一直存在于内存中,这会占用大量的内存,并导致性能下降。如果一个单例在应用程序的整个生命周期内都需要存在,就不需要担心内存占用和性能的问题。但是如果一个单例可以在某个时刻被释放掉,就需要根据应用程序的需要进行优化。
使用@autoreleasepool
语句来释放过多占用空间的对象
iOS内存管理并不是一项简单的任务。需要时刻关注内存的使用情况,找出内存泄漏的问题等等。本文介绍了iOS内存管理的一些基础概念,如堆和栈,引用计数以及ARC等,并提供了一些避免内存泄漏和进行内存管理的技巧。对iOS开发者而言,适当的内存管理不仅能够提高应用程序的性能,同时也能够避免不必要的错误和问题。