我如何比较闭包/块?

Posted

技术标签:

【中文标题】我如何比较闭包/块?【英文标题】:How can I compare closures/blocks? 【发布时间】:2020-05-16 14:23:40 【问题描述】:

鉴于应用程序使用的 ObjC 中的这段代码,我想将其转换为 Swift:

typedef void (^VoidBlock)(void);

@interface Worker : NSObject

- (void)add:(VoidBlock)completion;
- (void)remove:(VoidBlock)completion;
- (void)execute;

@end


@implementation Worker 
   NSMutableArray<VoidBlock> *completions;


- (void)add:(VoidBlock)completion 
   [completions addObject:completion];


- (void)remove:(VoidBlock)completion 
   [completions removeObject:completion];


- (void)execute 
    // ...
    [completions enumerateObjectsUsingBlock:^(VoidBlock  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) 
       obj();
    ];

@end

我在实施移除时遇到问题。我尝试了几件事,但没有。基本上,我无法比较 Swift 中的闭包,因为显然闭包不符合 Equatable 协议,因此无法在数组中找到它。或许内存地址可以用来做这个,不知道是不是objc指针和swift引用有问题。理论上,这段代码可以从 objc 或 swift 中使用。我的另一个想法是使用类似的东西:

let firstIndex = hardWorkers.firstIndex(where: 
    $0 === hardWorkerClosure
)

请注意身份运算符,但这不起作用。一位朋友告诉我,也许我必须使用容器来关闭/阻塞。我记得在 Objc 中,我们有 NSValue 将原始类型包装在一个类中,因此,我们可以将它们保存在 NSMutableArray 中,但我不记得有任何保存块的东西。您对如何解决此问题有任何想法或建议吗?非常感谢。

【问题讨论】:

您能否展示一下您从 Objective-C 成功转换的 Swift 代码部分? 闭包没有可比性,也没有定义的标识,以便给编译器更多的发挥空间,优化和合并闭包实现。如果您需要将身份与闭包相关联,您需要自己执行此操作:***.com/a/45284442/3141234 这能回答你的问题吗? In Swift 3, what is a way to compare two closures? @Ricardo 在 ObjC 中,块通常由它们的地址处理。您将指向它们的指针传递给 ARC(如果您使用手动内存管理)、复制它们等。在 Swift 中,它们有意将闭包标识暴露给“用户空间”Swift 代码.动机在这里:***.com/a/25694072/3141234 Swift 闭包也不仅仅是一个指针:forums.swift.org/t/… 【参考方案1】:

闭包在 Swift 中没有标识,这意味着您不能使用 === 来确定两个闭包是否指向相同的代码。如果你真的需要这个,一种解决方案是在闭包上构建一个抽象:

typealias VoidClosure = () -> Void

class VoidClosureRef 
    let body: VoidClosure

    init(_ body: @escaping VoidClosure) 
        self.body = body
    

然后您可以传播VoidClosureRef 的实例:

class Worker 
    private var completions: [VoidClosureRef] = []

    func add(_ completion: VoidClosureRef)  
        completions.append(completion)
    
    
    func remove(_ completion: VoidClosureRef)  
        completions.removeAll  $0 === completion 
    

但是,这里更好的设计是add 方法返回一个标识符,该标识符以后可用于移除闭包。

typealias VoidClosure = () -> Void

class Worker 
    private var completions: [UUID: VoidClosure] = [:]

    func add(_ completion: @escaping VoidClosure) -> UUID 
        let uuid = UUID()
        completions[uuid] = completion
        return uuid
    
    
    func remove(_ uuid: UUID) 
        completions.removeValue(forKey: uuid)
    

【讨论】:

以上是关于我如何比较闭包/块?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift / Xcode 12.3 中重用代码块进行闭包

Swift 学习- 08 -- 闭包

swift闭包新手详解(新手必看)

Swift之“闭包”的应用

Swift学习笔记-继续学习闭包

Swift学习笔记-继续学习闭包