Swift 中系统 API delegate 参数一定要在当前类实现传 self 吗 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jiangzm
V2EX    Swift

Swift 中系统 API delegate 参数一定要在当前类实现传 self 吗

  •  
  •   jiangzm 2024-09-29 22:25:35 +08:00 2721 次点击
    这是一个创建于 374 天前的主题,其中的信息可能已经有所发展或是发生改变。

    因为要写原生插件,swift 刚接触,语法比 objc 好看多了。

    在开发相机组件时遇到一个问题,AVCapturePhotoCaptureDelegate 契约用自定义 UIView/UIViewController 实现没问题, 换成独立的类实现就回调不了,不是什么原因。

    上代码:

    TakePhotoDelegate.swift

    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}"); } } 

    CameraxView.swift

    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) } // ... } 

    点击拍照按钮能听到快门声,但是 photoOutput 没有回调

    如果换成如下自定义 UIView 实现 AVCapturePhotoCaptureDelegate 没有问题,能正常回调

    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}"); } // ... } 

    有这个差异的原因是因为系统 API 的限制吗还是 swift 语言层面问题, 如果 takePhoto 方法希望传闭包参数回调拍照结果,正确应该怎么写。

    (回调参数赋值给实例对象, 个人感觉是个很奇怪的做法)

    8 条回复    2025-07-04 20:51:11 +08:00
    hguandl
        1
    hguandl  
       2024-09-29 23:09:23 +08:00
    我推测这与弱引用有关。capturePhoto 不会保持对 delegate 的引用,导致其在 takePhoto 结尾被直接释放掉了。可以试一试给 TakePhotoWithCompletion 加个自定义 deinit ,打一下日志或断点看一下释放时机。
    jiangzm
        2
    jiangzm  
    OP
       2024-09-30 10:48:52 +08:00
    @hguandl #1 是弱引用问题, 加了 deinit 有打印出来。 那除了强引用设置成实例成员还有其他方式解决么。
    一般用闭包封装委托是怎么做的。感觉我的写法和这个例子差不多 https://stackoverflow.com/questions/24173210/implement-delegate-with-closure-in-swift
    hguandl
        3
    hguandl  
       2024-09-30 11:10:52 +08:00
    @jiangzm delegate 这种设计模式 + Swift 的 ARC 特性决定了几乎只能这样写,不然就会造成内存泄漏。如果是纯 Swift 应用的话可以封装成 async 函数来解决,但是看你是要和桥接 objc ,那就没有太好的方法。
    louiswang002
        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 了
    jiangzm
        5
    jiangzm  
    OP
       2024-09-30 13:03:20 +08:00
    不知道为啥 swift 要搞一个 delegate 概念出来,也不是语法关键字 就是普通的 class 实现接口(契约) 。既然方法参数和属性能传函数(闭包),没有委托好像也行。发现有些系统 api 原来是传函数的,新版搞个新 class 新 api 就要传委托。^_^

    这个和 C#的 delegate 完全不同,委托是函数引用/指针,匿名函数(swift 闭包)、lambda 表达式、事件都是基于委托的。

    Swift 应该是差一个事件的概念, 但是呢没有一步到位整了个中间的 delegate 概念,再加上内存管理不是那么智能,使用起来有点束手束脚的感觉。

    C#中的委托和事件都是可以多播的,大部分组件对外暴露的都是事件,拿上面委托例子来说就是这样:

    ``` imageOutPut.OnPhotoOutput= () => { xxx }; ```

    或者
    ```imageOutPut.onPhotoOutput += () => { xxx }; ```
    Sricecake
        6
    Sricecake  
       2024-10-08 19:24:02 +08:00
    因为这些 API 是 OC 时代定义的,OC 当时没有闭包,所以只能以这种类似 JAVA 的 Interface 方式实现回调,你虽然使用的是 Swift ,但是其实还是在调用原 OC 的库。
    LFL
        7
    LFL  
       98 天前
    用单利搞
    jiangzm
        8
    jiangzm  
    OP
       97 天前
    @LFL #7 把 TakePhotoWithCompletion 写成单例吗, 那这样没法动态改变回调函数, 因为回调处理逻辑基于上下文数据
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2830 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 13:33 PVG 21:33 LAX 06:33 JFK 09:33
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86