因为要写原生插件,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 62 天前
我推测这与弱引用有关。capturePhoto 不会保持对 delegate 的引用,导致其在 takePhoto 结尾被直接释放掉了。可以试一试给 TakePhotoWithCompletion 加个自定义 deinit ,打一下日志或断点看一下释放时机。
|
2
jiangzm OP @hguandl #1 是弱引用问题, 加了 deinit 有打印出来。 那除了强引用设置成实例成员还有其他方式解决么。
一般用闭包封装委托是怎么做的。感觉我的写法和这个例子差不多 https://stackoverflow.com/questions/24173210/implement-delegate-with-closure-in-swift |
3
hguandl 62 天前
@jiangzm delegate 这种设计模式 + Swift 的 ARC 特性决定了几乎只能这样写,不然就会造成内存泄漏。如果是纯 Swift 应用的话可以封装成 async 函数来解决,但是看你是要和桥接 objc ,那就没有太好的方法。
|
4
louiswang002 62 天前 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 53 天前
因为这些 API 是 OC 时代定义的,OC 当时没有闭包,所以只能以这种类似 JAVA 的 Interface 方式实现回调,你虽然使用的是 Swift ,但是其实还是在调用原 OC 的库。
|