You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义类调用UIImageWriteToSavedPhotosAlbum触发运行时异常求助

解决UIImageWriteToSavedPhotosAlbum调用时的NSInvalidArgumentException异常

嘿,我来帮你分析这个问题!你遇到的这个崩溃异常,根源出在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:43:11