内存优化-(二)-oc&swift

我们分别从Objective-C和Swift两个角度来讨论iOS内存优化。虽然两者都是iOS开发语言,但在内存管理上有一些不同之处。

一、共同原则(无论OC还是Swift)

  1. 减少内存占用:及时释放不用的对象,使用懒加载。

  2. 避免内存泄漏:注意循环引用。

  3. 优化内存使用模式:避免瞬间内存暴涨。

二、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. 图片处理

  • 使用UIImageimageNamed:方法会在内存中缓存图片,适用于频繁使用的小图。对于大图,使用imageWithContentsOfFile:,它不会缓存。

  • 图片解码会占用大量内存,可以考虑在后台线程进行解码,然后主线程显示。

5. 及时释放不再需要的对象

  • 将对象设置为nil可以释放引用(如果该对象没有被其他对象强引用)。

6. 使用轻量级对象

  • 比如使用CALayer代替UIView,如果不需要交互的话。

7. 避免使用__block导致的循环引用

在ARC下,__block也会导致强引用,可以使用__weak打破。

8. 使用NSDatammap读取大文件

对于大文件,使用内存映射方式读取,减少内存占用。

三、Swift内存优化

1. 使用值类型(Struct和Enum)

值类型在传递时会复制,但编译器会进行优化,不会总是复制。而且值类型存储在栈上,比堆上的引用类型管理更高效。

2. 避免循环引用

  • 使用weakunowned打破循环引用。

    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 特别注意事项:

  1. 手动管理CF对象(CFRelease)

  2. 注意MRC项目的retain/release平衡

  3. 避免在dealloc中发送消息

  4. 使用@autoreleasepool管理临时对象

  5. 注意NSNotificationCenter的观察者移除

Swift 特别注意事项:

  1. 优先使用值类型(Struct/Enum)

  2. 注意闭包捕获列表([weak self])

  3. 使用lazy延迟初始化

  4. 避免隐式解包可选型(!)

  5. 使用defer确保资源释放

两者通用原则:

  1. 使用合适的缓存策略

  2. 及时响应内存警告

  3. 大文件使用流式处理

  4. 图片使用降采样

  5. 定期使用Instruments检测内存泄漏

通过结合语言特性和最佳实践,可以显著降低内存使用,提升应用性能和稳定性。

相关推荐
想学后端的前端工程师3 小时前
【浏览器工作原理与性能优化指南:深入理解Web性能】
前端·性能优化
我要用代码向我喜欢的女孩表白3 小时前
对象存储路径文件1TB以上文件比对,go语言
ios·golang·xcode
2501_916007474 小时前
iPhone APP 性能测试怎么做,除了Instruments还有什么工具?
android·ios·小程序·https·uni-app·iphone·webview
2501_915106324 小时前
Windows 环境下有哪些可用的 iOS 上架工具, iOS 上架工具的使用方式
android·ios·小程序·https·uni-app·iphone·webview
冬奇Lab5 小时前
稳定性性能系列之六——Java异常与JE分析实战
android·性能优化·debug
Student_Zhang6 小时前
一个管理项目中所有弹窗的弹窗管理器(PopupManager)
前端·ios·github
denggun123456 小时前
使用 os_unfair_lock 替代 DispatchQueue?!
ios
冬奇Lab7 小时前
Android反模式警示录:System.exit(0)如何制造546ms黑屏
android·性能优化·debug
小小王app小程序开发7 小时前
招工招聘小程序开发全解析:全栈架构、核心模块实现与性能优化
性能优化·架构