因为要写原生插件,swift 刚接触,语法比 objc 好看多了。
在开发相机组件时遇到一个问题,AVCapturePhotoCaptureDelegate 契约用自定义 UIView/UIViewController 实现没问题, 换成独立的类实现就回调不了,不是什么原因。
上代码:
import Foundation import AVFoundation import UIKit public typealias TakePhotoCallback = ((String, String) -> Void)?; class TakePhotoWithCompletion : NSObject, AVCapturePhotoCaptureDelegate { // ... var completion: TakePhotoCallback; init(callback: TakePhotoCallback) { self.completion = callback; super.init(); } func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { // todo sth self.completion!("{file path}", "{error}"); } }
import AVFoundation import UIKit class CameraxView: UIView { // ... @objc func takePhoto(options: Dictionary<String, String>, completion: TakePhotoCallback) { let photoSettings = AVCapturePhotoSettings.init(format:[AVVideoCodecKey:AVVideoCodecType.jpeg]) let delegate = TakePhotoWithCompletion.init(callback: completion) imageOutPut?.capturePhoto(with:photoSettings, delegate: delegate) } // ... }
import AVFoundation import UIKit class CameraxView: UIView, AVCapturePhotoCaptureDelegate { // ... var completion: TakePhotoCallback = nil; @objc func takePhoto(options: Dictionary<String, String>) { let photoSettings = AVCapturePhotoSettings.init(format:[AVVideoCodecKey:AVVideoCodecType.jpeg]) self.completion = completion imageOutPut?.capturePhoto(with:photoSettings, delegate: self) } @objc func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { // todo sth self.completion!("{file path}", "{error}"); } // ... }
![]() | 1 hguandl 2024-09-29 23:09:23 +08:00 我推测这与弱引用有关。capturePhoto 不会保持对 delegate 的引用,导致其在 takePhoto 结尾被直接释放掉了。可以试一试给 TakePhotoWithCompletion 加个自定义 deinit ,打一下日志或断点看一下释放时机。 |
![]() | 2 jiangzm OP @hguandl #1 是弱引用问题, 加了 deinit 有打印出来。 那除了强引用设置成实例成员还有其他方式解决么。 一般用闭包封装委托是怎么做的。感觉我的写法和这个例子差不多 https://stackoverflow.com/questions/24173210/implement-delegate-with-closure-in-swift |
![]() | 3 hguandl 2024-09-30 11:10:52 +08:00 @jiangzm delegate 这种设计模式 + Swift 的 ARC 特性决定了几乎只能这样写,不然就会造成内存泄漏。如果是纯 Swift 应用的话可以封装成 async 函数来解决,但是看你是要和桥接 objc ,那就没有太好的方法。 |
4 louiswang002 2024-09-30 11:29:28 +08:00 via iPhone let delegate = TakePhotoWithCompletion.init(callback: completion) 创建的对象没有引用,imageOutPut 对 delegate 是弱引用,当前栈弹出时,delegate 对象就被释放了,肯定不会有输出;你在第二种实现中,对 delegate 对象创建了一个强引用,imageOutPut 对 delegate 的弱引用对象也就存在了,也能触发 delegate 了 |
![]() | 5 jiangzm OP 不知道为啥 swift 要搞一个 delegate 概念出来,也不是语法关键字 就是普通的 class 实现接口(契约) 。既然方法参数和属性能传函数(闭包),没有委托好像也行。发现有些系统 api 原来是传函数的,新版搞个新 class 新 api 就要传委托。^_^ 这个和 C#的 delegate 完全不同,委托是函数引用/指针,匿名函数(swift 闭包)、lambda 表达式、事件都是基于委托的。 Swift 应该是差一个事件的概念, 但是呢没有一步到位整了个中间的 delegate 概念,再加上内存管理不是那么智能,使用起来有点束手束脚的感觉。 C#中的委托和事件都是可以多播的,大部分组件对外暴露的都是事件,拿上面委托例子来说就是这样: ``` imageOutPut.OnPhotoOutput= () => { xxx }; ``` 或者 ```imageOutPut.onPhotoOutput += () => { xxx }; ``` |
6 Sricecake 2024-10-08 19:24:02 +08:00 因为这些 API 是 OC 时代定义的,OC 当时没有闭包,所以只能以这种类似 JAVA 的 Interface 方式实现回调,你虽然使用的是 Swift ,但是其实还是在调用原 OC 的库。 |
![]() | 7 LFL 98 天前 用单利搞 |