自定义类调用UIImageWriteToSavedPhotosAlbum触发运行时异常求助
嘿,我来帮你分析这个问题!你遇到的这个崩溃异常,根源出在UIImageWriteToSavedPhotosAlbum的回调方法签名不符合系统要求上,咱们一步步拆解:
问题重现
你创建了PBExtensionExport类来实现图片导出功能,代码如下:
import Foundation import UIKit class PBExtensionExport: NSObject { static let instance = PBExtensionExport() fileprivate var progress: Int = 0 func exportPhotos(images: [UIImage], completion: (Bool) -> ()) { for image in images { UIImageWriteToSavedPhotosAlbum(image, self, #selector(updateStatus), nil) // Progress monitoring completion(images.count == progress) } } @objc private func updateStatus() { // Updating progress status progress += 1 } }
主流程调用代码:
PBExtensionExport.instance.exportPhotos(images: selectedImages, completion: { (success) -> Void in if success { // Do something } else { } })
运行时抛出异常:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSInvocation setArgument:atIndex:]: index (2) out of bounds [-1, 1]'
问题原因
UIImageWriteToSavedPhotosAlbum是一个Cocoa API,它要求你指定的回调方法必须遵循固定的参数签名:
- 第一个参数:保存的
UIImage对象 - 第二个参数:保存操作的错误信息(可选)
- 第三个参数:上下文信息(
UnsafeRawPointer类型)
你的updateStatus()是无参数方法,当系统尝试通过NSInvocation给这个方法传递参数时,找不到对应索引的参数位置,直接触发了数组越界的异常。
另外,你原代码里还有个逻辑问题:在循环中每次调用completion,此时进度还没更新,会导致completion被多次调用,而且判断逻辑完全错误。
解决方案
1. 修正回调方法的参数签名
必须按照系统要求声明回调方法,即使你用不上某些参数,也要保留它们的位置:
2. 调整进度判断与completion调用时机
把completion保存为实例变量,在所有图片都保存完成(或出现错误)时再调用它,同时重置进度避免下次调用出错。
修改后的完整代码:
import Foundation import UIKit class PBExtensionExport: NSObject { static let instance = PBExtensionExport() fileprivate var progress: Int = 0 // 保存completion回调 fileprivate var completionHandler: ((Bool) -> ())? // 记录需要导出的图片总数 fileprivate var totalImagesToExport: Int = 0 func exportPhotos(images: [UIImage], completion: @escaping (Bool) -> ()) { // 重置状态 progress = 0 totalImagesToExport = images.count completionHandler = completion // 空数组直接返回成功 guard totalImagesToExport > 0 else { completion(true) return } for image in images { UIImageWriteToSavedPhotosAlbum( image, self, #selector(updateStatus(image:didFinishSavingWithError:contextInfo:)), nil ) } } // 符合系统要求的回调方法签名 @objc private func updateStatus(image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) { // 如果出现错误,直接标记失败并清理状态 if let error = error { print("图片保存失败:\(error.localizedDescription)") completionHandler?(false) completionHandler = nil progress = 0 return } // 更新进度 progress += 1 // 所有图片保存完成时调用completion if progress == totalImagesToExport { completionHandler?(true) completionHandler = nil progress = 0 } } }
这样修改后,不仅解决了崩溃问题,还修正了进度判断的逻辑,确保completion只会在所有操作完成后调用一次。
内容的提问来源于stack exchange,提问作者victranit

