我们分别从Objective-C和Swift两个角度来讨论iOS内存优化。虽然两者都是iOS开发语言,但在内存管理上有一些不同之处。
一、共同原则(无论OC还是Swift)
-
减少内存占用:及时释放不用的对象,使用懒加载。
-
避免内存泄漏:注意循环引用。
-
优化内存使用模式:避免瞬间内存暴涨。
二、Objective-C内存优化
1. 使用自动释放池(Autorelease Pool)
在循环中创建大量临时对象时,使用自动释放池及时释放内存。
for (int i = 0; i < 10000; i++) {
@autoreleasepool {
// 创建大量临时对象
NSString *temp = [NSString stringWithFormat:@"%d", i];
// 使用temp
}
}
2. 避免Retain Cycles(循环引用)
-
使用
weak打破强引用环。__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf doSomething];
}; -
对于delegate属性,通常使用
weak。@property (nonatomic, weak) id<MyDelegate> delegate;
3. 缓存优化
- 使用
NSCache代替NSDictionary进行缓存,因为NSCache会在内存紧张时自动释放对象。
4. 图片处理
-
使用
UIImage的imageNamed:方法会在内存中缓存图片,适用于频繁使用的小图。对于大图,使用imageWithContentsOfFile:,它不会缓存。 -
图片解码会占用大量内存,可以考虑在后台线程进行解码,然后主线程显示。
5. 及时释放不再需要的对象
- 将对象设置为
nil可以释放引用(如果该对象没有被其他对象强引用)。
6. 使用轻量级对象
- 比如使用
CALayer代替UIView,如果不需要交互的话。
7. 避免使用__block导致的循环引用
在ARC下,__block也会导致强引用,可以使用__weak打破。
8. 使用NSData的mmap读取大文件
对于大文件,使用内存映射方式读取,减少内存占用。
三、Swift内存优化
1. 使用值类型(Struct和Enum)
值类型在传递时会复制,但编译器会进行优化,不会总是复制。而且值类型存储在栈上,比堆上的引用类型管理更高效。
2. 避免循环引用
-
使用
weak或unowned打破循环引用。class MyClass {
var closure: (() -> Void)?func setup() { closure = { [weak self] in self?.doSomething() } }}
3. 使用懒加载(Lazy)
延迟创建对象直到第一次使用。
lazy var expensiveObject: ExpensiveClass = {
return ExpensiveClass()
}()
4. 使用自动释放池
虽然Swift使用ARC,但 autoreleasepool 仍然有用,尤其是在循环中创建大量临时对象时。
for _ in 0...10000 {
autoreleasepool {
let temp = NSString(format: "%d", i)
// 使用temp
}
}
5. 图片处理
-
使用
UIGraphicsImageRenderer代替UIGraphicsBeginImageContext,更高效且自动管理内存。 -
对于大图,使用Image I/O进行降采样(downsampling)。
6. 使用defer确保资源释放
func processFile() {
let file = openFile()
defer {
closeFile(file)
}
// 使用file
}
7. 注意闭包捕获列表
避免在闭包中意外捕获self导致循环引用。
8. 使用unowned的注意事项
只有当你确定对象不会在闭包执行期间被释放时才使用unowned,否则会导致野指针。
四、工具使用(两者通用)
-
Instruments:Allocations, Leaks, VM Tracker, Memory Graph Debugger。
-
Debug Memory Graph:可视化对象引用关系。
五、内存警告处理
在Objective-C中:
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// 释放非关键资源
}
在Swift中:
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// 释放非关键资源
}
六、注意事项
-
Objective-C :注意
@property的属性(strong, weak, copy等)选择。 -
Swift:注意值类型和引用类型的选择,以及闭包捕获列表。
七、示例对比
循环引用示例
Objective-C:
// 打破循环引用
__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf doSomething];
};
Swift:
// 打破循环引用
self.block = { [weak self] in
self?.doSomething()
}
懒加载示例
Objective-C:
// 懒加载属性
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
- (NSDateFormatter *)dateFormatter {
if (!_dateFormatter) {
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateFormat = @"yyyy-MM-dd";
}
return _dateFormatter;
}
Swift:
lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
八、总结
无论是Objective-C还是Swift,内存优化的核心思想都是相似的:减少占用、及时释放、避免泄漏。但是,由于Swift在语言层面提供了更多值类型和现代语法,我们在Swift中可以通过合理使用值类型、懒加载等来优化内存。而在Objective-C中,我们更依赖于手动管理引用计数和自动释放池。
在实际开发中,我们应该根据项目使用的语言,结合工具进行 profiling,找到内存瓶颈,然后针对性地优化。
iOS内存优化:Objective-C与Swift双重视角
1. 内存管理模型
-
Objective-C: MRC/ARC (手动/自动引用计数)
-
Swift: ARC (自动引用计数) + 值类型优化
2. 诊断工具通用
-
Instruments: Allocations, Leaks, VM Tracker
-
Xcode Memory Graph Debugger
-
Debug Memory Graph按钮
二、Objective-C 内存优化
1. 循环引用处理
// 1. Delegate使用weak
@property (nonatomic, weak) id<MyDelegate> delegate;
// 2. Block中的循环引用
__weak typeof(self) weakSelf = self;
self.completionHandler = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf doSomething];
};
// 3. NSTimer循环引用(iOS 10+使用block API)
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf handleTimer];
}];
// 4. 手动打破循环(dealloc中)
- (void)dealloc {
[_timer invalidate];
_timer = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
2. 自动释放池优化
// 大量临时对象创建时
for (int i = 0; i < 10000; i++) {
@autoreleasepool {
NSString *temp = [NSString stringWithFormat:@"%d", i];
// 使用temp...
}
}
3. 单例内存优化
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static MyClass *instance = nil;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
// 避免在初始化方法中创建大量数据
});
return instance;
}
// 添加内存警告响应
- (void)clearCacheOnMemoryWarning {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMemoryWarning)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
- (void)handleMemoryWarning {
// 清理可重建的缓存
[_cache removeAllObjects];
}
4. Core Foundation对象管理
// 手动管理CF对象
CFArrayRef array = CFArrayCreate(NULL, NULL, 0, NULL);
// 使用...
CFRelease(array); // 必须手动释放
// Core Graphics对象
CGContextRef context = UIGraphicsGetCurrentContext();
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef); // 手动释放
5. KVO观察者释放
- (void)dealloc {
// 必须移除观察者
[object removeObserver:self forKeyPath:@"property"];
// iOS 11+ 或使用安全的移除方式
@try {
[object removeObserver:self forKeyPath:@"property"];
} @catch (NSException *exception) {
NSLog(@"移除观察者异常: %@", exception);
}
}
6. 大对象处理技巧
// 1. 文件映射读取大文件
NSData *data = [NSData dataWithContentsOfFile:filePath
options:NSDataReadingMappedIfSafe
error:nil];
// 2. 流式处理大文件
NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
[inputStream open];
uint8_t buffer[1024];
while ([inputStream read:buffer maxLength:sizeof(buffer)] > 0) {
// 处理数据
}
[inputStream close];
三、Swift 内存优化
1. 值类型优化
// 1. 使用结构体替代类(栈分配,无引用计数开销)
struct Point {
var x: Double
var y: Double
// 所有方法默认 nonmutating
}
// 2. 写时复制(Copy-on-Write)优化
struct LargeData {
private var _storage: NSMutableData
init(data: NSData) {
_storage = data.mutableCopy() as! NSMutableData
}
mutating func append(_ other: NSData) {
if !isKnownUniquelyReferenced(&_storage) {
_storage = _storage.mutableCopy() as! NSMutableData
}
_storage.append(other)
}
}
// 3. 使用枚举减少内存占用
enum Status {
case idle
case loading(progress: Float)
case completed(data: Data)
case failed(error: Error?)
}
2. 引用类型优化
// 1. 弱引用和无主引用
class MyClass {
// 闭包捕获列表
lazy var closure: () -> Void = { [weak self] in
guard let self = self else { return }
self.doSomething()
}
// unowned - 当确保引用对象生命周期更长时使用
unowned let parent: ParentClass
// 使用deinit清理资源
deinit {
NotificationCenter.default.removeObserver(self)
}
}
// 2. 协议优化 - 使用关联类型而非Any
protocol Processor {
associatedtype Input
associatedtype Output
func process(_ input: Input) -> Output
}
// 3. 使用final类和private访问控制
final class OptimizedClass {
private var internalData: [Int]
// 编译器可以进行更好的优化
}
3. 集合类型优化
// 1. 使用ContiguousArray提高性能
var array = ContiguousArray<Int>() // 连续内存分配
// 2. 预分配容量
var largeArray = [Int]()
largeArray.reserveCapacity(10000)
// 3. 使用lazy序列
let numbers = (1...1000000).lazy
.filter { $0 % 2 == 0 }
.map { $0 * 2 }
// 惰性求值,不会立即创建中间数组
4. 图片和数据处理
// 1. 使用ImageIO进行图片降采样
func downsample(imageAt url: URL, to size: CGSize) -> UIImage? {
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, imageSourceOptions) else { return nil }
let maxDimension = max(size.width, size.height)
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimension,
kCGImageSourceCreateThumbnailWithTransform: true
] as CFDictionary
guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else { return nil }
return UIImage(cgImage: downsampledImage)
}
// 2. 使用DispatchData处理大数据
let largeData = Data(count: 10_000_000)
let dispatchData = largeData.withUnsafeBytes { bytes in
DispatchData(bytes: bytes)
}
5. Swift与Objective-C混编优化
// 1. 使用@objcMembers减少桥接开销
@objcMembers class BridgedClass: NSObject {
dynamic var property: String = "" // dynamic启用动态分发
}
// 2. 使用String而非NSString
let swiftString = String("Hello") // 值类型,栈分配
let nsString: NSString = "Hello" // 引用类型,堆分配
// 3. 避免频繁的Swift-OC桥接
let array: [String] = ["a", "b", "c"]
let nsArray = array as NSArray // 桥接一次,多次使用
四、高级优化技术(两者通用)
1. 内存映射文件
// Objective-C
NSString *filePath = @"large_file.bin";
int fd = open([filePath UTF8String], O_RDONLY);
void *mapped = mmap(NULL, fileSize, PROT_READ, MAP_PRIVATE, fd, 0);
// 使用mapped指针直接访问文件内容
munmap(mapped, fileSize);
close(fd);
// Swift
import Foundation
func mapFile(at path: String) throws -> Data {
let url = URL(fileURLWithPath: path)
return try Data(contentsOf: url, options: .alwaysMapped)
}
2. 自定义内存分配器
// Objective-C - 使用内存池
@interface MemoryPool : NSObject
- (void *)allocate:(size_t)size;
- (void)reset;
@end
// 在大量小对象分配时减少malloc调用
3. 缓存策略优化
// 使用NSCache(两者通用)
class ImageCache {
private let cache = NSCache<NSString, UIImage>()
private let costLimit: Int
init(costLimit: Int = 1024 * 1024 * 100) { // 100MB
self.costLimit = costLimit
cache.totalCostLimit = costLimit
cache.countLimit = 100 // 对象数量限制
// 内存警告时自动清理
NotificationCenter.default.addObserver(
forName: UIApplication.didReceiveMemoryWarningNotification,
object: nil,
queue: .main
) { [weak self] _ in
self?.cache.removeAllObjects()
}
}
func setImage(_ image: UIImage, for key: String) {
let cost = Int(image.size.width * image.size.height * 4) // 估算内存成本
cache.setObject(image, forKey: key as NSString, cost: cost)
}
}
4. 懒加载模式
// Objective-C
@property (nonatomic, strong, readonly) NSDateFormatter *dateFormatter;
- (NSDateFormatter *)dateFormatter {
if (!_dateFormatter) {
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateFormat = @"yyyy-MM-dd";
}
return _dateFormatter;
}
// Swift
class MyClass {
lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
// 线程安全的懒加载
lazy var threadSafeData: [String] = {
var data = [String]()
// 初始化代码
return data
}()
}
五、特定场景优化
1. TableView/CollectionView优化
// Objective-C - 复用cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"Cell";
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
// 一次性配置,避免重复创建
}
// 轻量级配置
[cell configureWithData:self.data[indexPath.row]];
return cell;
}
// 异步图片加载
- (void)loadImageForCell:(MyCell *)cell atIndexPath:(NSIndexPath *)indexPath {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [self loadImageFromDiskAtIndex:indexPath.row];
dispatch_async(dispatch_get_main_queue(), ^{
// 检查cell是否仍显示相同内容
if ([tableView.indexPathsForVisibleRows containsObject:indexPath]) {
cell.imageView.image = image;
}
});
});
}
2. Core Data优化
// Swift - 批量操作
func batchInsert(items: [ItemData]) {
let context = persistentContainer.newBackgroundContext()
context.perform {
let batchInsert = NSBatchInsertRequest(entity: Item.entity()) { (managedObject: NSManagedObject) in
// 配置对象
}
do {
try context.execute(batchInsert)
try context.save()
} catch {
print("批量插入失败: \(error)")
}
}
}
// 使用fetchLimit和fetchBatchSize
let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
fetchRequest.fetchLimit = 50
fetchRequest.fetchBatchSize = 20
fetchRequest.returnsObjectsAsFaults = false // 避免惰值加载
3. 网络响应处理
// Objective-C - 流式处理大响应
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data) {
// 使用流解析器而非一次性加载全部数据
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
}
}];
六、调试与监控
1. 内存警告处理
// Objective-C
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// 释放视图(如果当前不可见)
if (![self isViewLoaded] || self.view.window == nil) {
self.view = nil;
}
// 清理缓存
[self clearCaches];
}
// Swift
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// 释放后台资源
if view.window == nil {
// 清理视图相关资源
}
// 清理缓存
clearCaches()
}
2. 自定义内存统计
// Swift内存使用统计
func printMemoryUsage() {
var info = mach_task_basic_info()
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
task_info(mach_task_self_,
task_flavor_t(MACH_TASK_BASIC_INFO),
$0,
&count)
}
}
if kerr == KERN_SUCCESS {
let usedBytes = info.resident_size
let usedMB = Double(usedBytes) / 1024 / 1024
print("内存使用: \(usedMB) MB")
}
}
七、最佳实践总结
Objective-C 特别注意事项:
-
手动管理CF对象(CFRelease)
-
注意MRC项目的retain/release平衡
-
避免在dealloc中发送消息
-
使用
@autoreleasepool管理临时对象 -
注意
NSNotificationCenter的观察者移除
Swift 特别注意事项:
-
优先使用值类型(Struct/Enum)
-
注意闭包捕获列表([weak self])
-
使用lazy延迟初始化
-
避免隐式解包可选型(!)
-
使用
defer确保资源释放
两者通用原则:
-
使用合适的缓存策略
-
及时响应内存警告
-
大文件使用流式处理
-
图片使用降采样
-
定期使用Instruments检测内存泄漏
通过结合语言特性和最佳实践,可以显著降低内存使用,提升应用性能和稳定性。