// For licensing see accompanying LICENSE.md file. // Copyright (C) 2022 Apple Inc. All Rights Reserved. import CoreML /// A class to manage and gate access to a Core ML model /// /// It will automatically load a model into memory when needed or requested /// It allows one to request to unload the model from memory @available(iOS 16.2, macOS 13.1, *) public final class ManagedMLModel: ResourceManaging { /// The location of the model var modelURL: URL /// The configuration to be used when the model is loaded var configuration: MLModelConfiguration /// The loaded model (when loaded) var loadedModel: MLModel? /// Queue to protect access to loaded model var queue: DispatchQueue /// Create a managed model given its location and desired loaded configuration /// /// - Parameters: /// - url: The location of the model /// - configuration: The configuration to be used when the model is loaded/used /// - Returns: A managed model that has not been loaded public init(modelAt url: URL, configuration: MLModelConfiguration) { self.modelURL = url self.configuration = configuration self.loadedModel = nil self.queue = DispatchQueue(label: "managed.\(url.lastPathComponent)") } /// Instantiation and load model into memory public func loadResources() throws { try queue.sync { try loadModel() } } /// Unload the model if it was loaded public func unloadResources() { queue.sync { loadedModel = nil } } /// Perform an operation with the managed model via a supplied closure. /// The model will be loaded and supplied to the closure and should only be /// used within the closure to ensure all resource management is synchronized /// /// - Parameters: /// - body: Closure which performs and action on a loaded model /// - Returns: The result of the closure /// - Throws: An error if the model cannot be loaded or if the closure throws public func perform(_ body: (MLModel) throws -> R) throws -> R { return try queue.sync { try autoreleasepool { try loadModel() return try body(loadedModel!) } } } private func loadModel() throws { if loadedModel == nil { loadedModel = try MLModel(contentsOf: modelURL, configuration: configuration) } } }