Swift 3/4内存不足时对象创建的处理、检测及异常处理
Great question—dealing with low-memory scenarios in Swift (especially versions 3 and 4) is critical when you're working with memory-heavy objects like your MyClass example. Let's break down how to detect failures, handle exceptions, and mitigate these issues step by step.
1. How to Detect if Object Creation Failed?
When you try to allocate a huge chunk of memory (like your 100-million-element array), two things can happen:
- First, the system sends a memory warning (on iOS/macOS). If your app doesn't free up resources quickly, the runtime will throw an
NSMallocException(an Objective-C-style exception, not a native SwiftError). - By default, Swift doesn't catch these exceptions automatically—so unhandled cases will crash your app. To detect failure, you need to explicitly catch these exceptions or pre-check memory availability before attempting creation.
2. Does Swift Have Corresponding Exception Handling Mechanisms?
Swift's native do-catch system works with Error-conforming types, but it doesn't handle Objective-C NSException out of the box. To catch memory allocation exceptions, you'll need to use a small Objective-C bridging helper (or a Swift wrapper around Objective-C's exception handling).
Additionally, you can pre-emptively check memory usage to avoid hitting exceptions entirely—this is often more reliable than catching crashes after the fact.
3. How to Handle Severe RAM Shortage Scenarios?
Here are practical strategies to deal with low memory when creating objects:
- Pre-check memory availability: Calculate how much memory your object needs (e.g., 100 million
UInt64values = ~800MB) and compare it to free system memory usingProcessInfo. This lets you fail gracefully before trying to allocate. - Batch processing: Instead of loading all data at once, split it into smaller chunks. This reduces your app's peak memory footprint significantly.
- Use
autoreleasepool: Wrap memory-intensive operations in an autorelease pool to force temporary objects to be released immediately, freeing up RAM faster. - Lazy initialization: Delay populating large data structures until they're actually needed (instead of doing it in
init()), so you don't waste memory if the object sits unused. - Catch exceptions gracefully: If you do attempt a large allocation, wrap it in an exception-catching block. If it fails, free up non-essential resources (like caches) and retry, or show a user-friendly error.
Example Code: Improved Memory Handling
First, a helper function to catch Objective-C exceptions in Swift:
import Foundation func catchException<T>(block: () throws -> T) -> T? { var result: T? objc_try { do { result = try block() } catch { result = nil } } objc_catch { exception in print("Memory allocation failed: \(exception.debugDescription)") result = nil } return result }
Then, a modified MyClass with memory safeguards:
class MyClass { var array: [UInt64] = [] // Failable initializer to handle memory failures init?(batchSize: Int = 1_000_000, totalCount: Int = 100_000_000) { // Calculate required memory (8 bytes per UInt64) let requiredMemory = UInt64(totalCount) * UInt64(MemoryLayout<UInt64>.stride) let freeMemory = ProcessInfo.processInfo.physicalMemory - ProcessInfo.processInfo.systemMemoryUsage // Pre-check if we have enough free memory guard freeMemory >= requiredMemory else { print("Not enough free memory to initialize the array") return nil } // Batch load to reduce peak memory usage for batchIndex in 0..<(totalCount / batchSize) { let start = batchIndex * batchSize let end = start + batchSize // Use autoreleasepool to free temp objects immediately autoreleasepool { let batchArray = (start..<end).map { UInt64($0) } array.append(contentsOf: batchArray) } // Check for memory pressure mid-processing let currentUsage = ProcessInfo.processInfo.systemMemoryUsage if currentUsage > ProcessInfo.processInfo.physicalMemory * 9 / 10 { print("Memory running critically low during batch processing") array.removeAll() // Free up what we've allocated return nil } } } func show() { array.forEach { print($0) } } } // Usage with error handling if let myClass = catchException { MyClass() } { myClass.show() } else { print("Failed to create MyClass instance due to low memory") }
Bonus: Listen for Memory Warnings
On iOS/macOS, you should also listen for system memory warnings to proactively free resources:
// In your app delegate or view controller NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: .main) { _ in print("Received memory warning—freeing non-essential resources") // Clear caches, empty unused arrays, etc. }
内容的提问来源于stack exchange,提问作者sulabh

