Swift 6中使用AWSS3Manager上传或删除图片时崩溃——是否为完成块(completion block)相关问题?
Swift 6中使用AWSS3Manager上传或删除图片时崩溃——是否为完成块(completion block)相关问题?
看起来你遇到的这个问题在Swift 6迁移后挺常见的,尤其是单例模式下处理S3操作时,完成块的生命周期和调用逻辑很容易出问题。我来帮你拆解可能的原因和解决思路:
- 完成块的循环引用问题
Swift 6对闭包的捕获规则做了更严格的检查,单例本身是全局强引用的,如果你的完成块里直接引用了self(比如更新UI、调用类内其他方法),很容易形成循环引用,导致内存泄漏或者对象意外释放后回调被触发,进而引发崩溃。
你可以检查AWSS3Manager里的上传/删除方法,是不是在闭包中用了self却没加弱引用?比如错误示例:
func uploadImage(_ image: UIImage, completion: @escaping (Result<String, Error>) -> Void) { transferUtility.upload(...) { task, error in if let error = error { completion(.failure(error)) } else { self.updateUploadCount() // 直接引用self,易出问题 completion(.success(task?.key ?? "")) } } }
改成弱引用的安全写法:
func uploadImage(_ image: UIImage, completion: @escaping (Result<String, Error>) -> Void) { transferUtility.upload(...) { [weak self] task, error in guard let self = self else { completion(.failure(NSError(domain: "AWSS3Manager", code: -1, userInfo: [NSLocalizedDescriptionKey: "Manager已释放"]))) return } if let error = error { completion(.failure(error)) } else { self.updateUploadCount() completion(.success(task?.key ?? "")) } } }
- 批量上传时的线程安全与重复回调问题
如果你的uploadImages是循环调用单个上传接口,没做好并发控制和状态管理,很可能出现多个完成块同时触发,导致数据竞争(比如多线程同时修改同一个上传计数变量),或者失败后completion被多次调用的情况。
比如这种写法就有风险:
func uploadImages(_ images: [UIImage], completion: @escaping (Result<[String], Error>) -> Void) { var uploadedKeys: [String] = [] var error: Error? for image in images { uploadImage(image) { result in switch result { case .success(let key): uploadedKeys.append(key) if uploadedKeys.count == images.count { completion(.success(uploadedKeys)) } case .failure(let err): if error == nil { error = err completion(.failure(err)) } } } } }
可以用DispatchGroup管理并发,再加线程锁保证数据安全:
func uploadImages(_ images: [UIImage], completion: @escaping (Result<[String], Error>) -> Void) { let group = DispatchGroup() var uploadedKeys: [String] = [] var error: Error? let lock = NSLock() for image in images { group.enter() uploadImage(image) { result in lock.lock() defer { lock.unlock() group.leave() } guard error == nil else { return } // 已有错误时直接返回,避免重复回调 switch result { case .success(let key): uploadedKeys.append(key) case .failure(let err): error = err } } } group.notify(queue: .main) { if let error = error { completion(.failure(error)) } else { completion(.success(uploadedKeys)) } } }
- S3 TransferUtility的生命周期问题
Swift 6对对象生命周期管理更严格,如果你的transferUtility是单例内部初始化,但没有被强引用或者意外释放,可能导致上传/删除任务中途取消,完成块被异常触发。
确保单例里的transferUtility是强引用属性:
class AWSS3Manager { static let shared = AWSS3Manager() private let transferUtility: AWSS3TransferUtility // 强引用 private init() { let configuration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "your-pool-id")) AWSServiceManager.default().defaultServiceConfiguration = configuration self.transferUtility = AWSS3TransferUtility.default() } // ...其他方法 }
- 排查完成块是否被多次调用
你可以在完成块里加日志,记录每次调用的情况:
uploadImage(image) { result in print("上传完成块调用:\(result)") // 原逻辑 }
这样能确认是不是真的存在重复调用,如果是,检查下是不是循环里不小心重复添加了S3任务。
备注:内容来源于stack exchange,提问作者Bhavin Parghi
相关产品推荐
相关产品推荐

