【问题标题】: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 代码部分?
  • 闭包没有可比性,也没有定义的标识,以便给编译器更多的发挥空间,优化和合并闭包实现。如果您需要将身份与闭包相关联,您需要自己执行此操作:stackoverflow.com/a/45284442/3141234
  • @Ricardo 在 ObjC 中,块通常由它们的地址处理。您将指向它们的指针传递给 ARC(如果您使用手动内存管理)、复制它们等。在 Swift 中,它们有意将闭包标识暴露给“用户空间”Swift 代码.动机在这里:stackoverflow.com/a/25694072/3141234
  • Swift 闭包也不仅仅是一个指针:forums.swift.org/t/…

标签: objective-c swift


【解决方案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)
    }
}

【讨论】:

    猜你喜欢
    • 2015-06-15
    • 1970-01-01
    • 2013-09-16
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多