我有一个通用委托(delegate) ProducerDelegate 的问题,它将有一个与消费者 IntConsumer 相同类型的参数 (Int)方法需要它 (Int)
如果将调用委托(delegate)方法并且我想使用接收到的值 element
func didProduce<Int>(from: Producer<Int>, element: Int) {
output(element: element)
}
调用其他方法时出现错误:
无法将“Int”类型的值转换为预期的参数类型“Int”
我的问题是为什么?
我解释一下我的情况(这里是一个具有相同来源的 playground 文件:http://tuvalu.s3.amazonaws.com/so/generic-delegate.playground.zip)
我有一个通用的生产者类Producer,它带有生产元素ProducerDelegate的协议(protocol):
import Foundation
/// Delegate for produced elements
protocol ProducerDelegate : class {
/// Called if a new element is produced
///
/// - Parameters:
/// - from: producer
/// - element: produced element
func didProduce<T>(from: Producer<T>, element: T)
}
/// Produces new element
class Producer<T> {
/// The object that acts as consumer of produced element
weak var delegate: ProducerDelegate?
/// The producing element
let element: T
/// Initializes and returns a `Producer` producing the given element
///
/// - Parameters:
/// - element: An element which will be produced
init(element: T) {
self.element = element
}
/// Produces the object given element
func produce() {
delegate?.didProduce(from: self, element: element)
}
}
在消费者中,生产者被注入(inject):
/// Consumes produced `Int` elements and work with it
class IntConsumer {
/// Producer of the `Int`s
let producer: Producer<Int>
/// Initializes and returns a `IntConsumer` having the given producer
///
/// - Parameters:
/// - producer: `Int` producer
init(producer: Producer<Int>) {
self.producer = producer
self.producer.delegate = self
}
/// outputs the produced element
fileprivate func output(element: Int) {
print(element)
}
}
现在,我想像这样为委托(delegate)添加扩展:
extension IntConsumer: ProducerDelegate {
func didProduce<Int>(from: Producer<Int>, element: Int) {
output(element: element)
}
}
但是,它失败了:
无法将“Int”类型的值转换为预期的参数类型“Int”
Swift 编译器说我应该将元素转换为 Int,例如:
func didProduce<Int>(from: Producer<Int>, element: Int) {
output(element: element as! Int)
}
但也失败了
但是,如果泛型类型有其他具体类型,比如 String,我可以强制转换并且它有效:
func didProduce<String>(from: Producer<String>, element: String) {
guard let element2 = element as? Int else { return }
output(element: element2)
}
因此,我目前的解决方案是使用类型别名,这样我就不必在委托(delegate)方法中输入错误的类型:
extension IntConsumer: ProducerDelegate {
typealias T = Int
func didProduce<T>(from: Producer<T>, element: T) {
guard let element = element as? Int else { return }
output(element: element)
}
}
我希望有人能解释我的错误并给我一个更好的解决方案。
最佳答案
您的协议(protocol)要求
func didProduce<T>(from: Producer<T>, element: T)
说“我可以用任何类型的元素和相同类型元素的生产者来调用”。但这不是你想要表达的——IntConsumer 只能消耗Int元素。
然后您将此要求实现为:
func didProduce<Int>(from: Producer<Int>, element: Int) {...}
它定义了一个名为“Int”的新通用占位符——它将隐藏标准库的Int在方法里面。因为您的“Int”可以表示任何类型,编译器正确地告诉您不能将它传递给需要实际 Int 的参数。 .
这里你不需要泛型——你想要一个 associated type相反:
/// Delegate for produced elements
protocol ProducerDelegate : class {
associatedtype Element
/// Called if a new element is produced
///
/// - Parameters:
/// - from: producer
/// - element: produced element
func didProduce(from: Producer<Element>, element: Element)
}
这个协议(protocol)要求现在说“我只能用特定类型的元素调用,这将由符合类型决定”。
然后您可以简单地将需求实现为:
extension IntConsumer : ProducerDelegate {
// Satisfy the ProducerDelegate requirement – Swift will infer that
// the associated type "Element" is of type Int.
func didProduce(from: Producer<Int>, element: Int) {
output(element: element)
}
}
(注意删除了 <Int> 通用占位符)。
但是,因为我们现在使用的是关联类型,所以您不能使用 ProducerDelegate作为实际类型——只是一个通用占位符。这是因为如果您仅根据 ProducerDelegate 进行讨论,编译器现在不知道关联的类型是什么。 ,因此您不可能使用依赖于该关联类型的协议(protocol)要求。
这个问题的一个可能的解决方案是定义一个 type erasure为了包装委托(delegate)方法,并允许我们根据通用占位符来表达关联类型:
// A wrapper for a ProducerDelegate that expects an element of a given type.
// Could be implemented as a struct if you remove the 'class' requirement from
// the ProducerDelegate.
// NOTE: The wrapper will hold a weak reference to the base.
class AnyProducerDelegate<Element> : ProducerDelegate {
private let _didProduce : (Producer<Element>, Element) -> Void
init<Delegate : ProducerDelegate>(_ base: Delegate) where Delegate.Element == Element {
_didProduce = { [weak base] in base?.didProduce(from: $0, element: $1) }
}
func didProduce(from: Producer<Element>, element: Element) {
_didProduce(from, element)
}
}
为了防止保留循环,base被类型删除弱捕获。
然后你会想要改变你的 Producer的 delegate使用此类型删除包装器的属性:
var delegate: AnyProducerDelegate<Element>?
然后在 IntConsumer 中分配委托(delegate)时使用包装器:
/// Consumes produced `Int` elements and work with it
class IntConsumer {
// ...
init(producer: Producer<Int>) {
self.producer = producer
self.producer.delegate = AnyProducerDelegate(self)
}
// ...
}
不过,这种方法的一个缺点是 delegate不会设置为 nil如果消费者被释放,而不是调用 didProduce它只会默默地失败。不幸的是,我不知道实现此目标的更好方法 - 如果其他人有更好的想法,我肯定会感兴趣。
关于 swift 3 : Type error of generic delegate type with concrete consumer type,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41319698/
当我将time.Now()编码到JSON对象时,它给出的结果为"2009-11-10T23:00:00Z"但打印时间。现在给出2009-11-1023:00:00+0000UTC。他们为什么不同。什么是T和Z。另外,如何根据this将其转换为swiftNSDate对象?表? 最佳答案 这些值的含义无关紧要,它们是该格式(ISO8601)的一部分。有几种方法可以解决这个问题。一种是为时间或您的结构定义自定义MarshalJSON()方法并使用它来格式化日期,另一种是首先在您的结构中将其表示为字符串,以便当默认实现执行你得到你正在寻找的
在C/C++/Java/Go中,我们使用,来分隔参数:(aint,bint)在ObjectiveC中,我们使用:来表示参数::(int)a:(int)b在Swift中,我们必须同时使用:和,:(a:int,b:int)是否需要冗余? 最佳答案 Swift可能有外部和内部参数名称:(externalinternal:Int)如果没有独特的分隔符,会产生很多歧义。 关于objective-c-为什么Swift函数定义语法是多余的?,我们在StackOverflow上找到一个类似的问题:
我的Swift应用程序与用Go编写的服务器通信。我希望使用Zlib压缩传输的数据,但压缩结果似乎与Swift和Go不同。这是Go版本:sourceString:="A-t-ellebesoind'autrespreuves?Acceptez-lapourleplaisir.J'aitantfaitquedelacueillir,Etc'estpresqueunefleur-des-veuves."//Compressionvarbbytes.Bufferwriter:=zlib.NewWriter(&b)writer.Write([]byte(sourceString))writer.
我正在使用rsaEncryptionOAEPSHA256AESGCM在iOS上使用SecKeyCreateEncryptedData加密一些数据,然后在golang后端解密相同的数据。我正在使用3072位rsa公钥来加密对称key。当我从iOS获取数据到后端时,我能够成功解密对称key,但gcm标签验证失败。我使用的是与iOS相同的16字节IV,但不知道iOS在加密时是否使用任何aad(附加身份验证数据)。有谁知道rsaEncryptionOAEPSHA256AESGCMforiOS是否使用了一些aad?这适用于iOS10+。我已经尝试过使用nil、空的16字节数组、aeskey本身、
我使用swiftyJSON从apiurl消费OData。这里的apiurl与VPN连接。并且apiurl看起来像http://192.xxx.xx.xx:8000/sap/opu/odata/sap/Z_SRV/PRListSetSet?$format=json当我在模拟器中运行时,我可以从odataapiurl获取数据,但是在设备中运行时,没有从odataapiurl接收到数据。由于没有vpn连接到移动设备。我如何以编程方式对我的VPN进行硬编码以在移动设备中接收数据?这是我如何从ODataapiurl获取数据:typealiasServiceResponse=(JSON,Error
我正在开发一个涉及一些XML解析的自定义框架,使用Kanna框架。每当我尝试将我的框架导入playground时,playground就会崩溃并出现以下错误:Playgroundexecutionfailed:expressionfailedtoparse,unknownerror*thread#1:tid=0x4e9448,0x00000001074bf360com.apple.dt.Xcode.PlaygroundStub-macosx`executePlayground,queue='com.apple.main-thread',stopreason=breakpoint1.1*
我尝试在Windows10上的Ubuntu上的bash上安装Swift4我的Ubuntu版本:我@DESKTOP:~$lsb_release-a没有可用的LSB模块。经销商ID:Ubuntu描述:Ubuntu16.04.3LTS发布:16.04代号:xenial我做了apt-getupgrade和apt-getupdate我遵循Linux步骤here:安装Swift4最终我得到了错误:我@DESKTOP:~$swift/home/me/swift4/swift-4.0.2-RELEASE-ubuntu16.04/usr/bin/lldb:加载共享库时出错:libpython2.7.so
概述音视频采集是直播架构的第一步音视频采集包括两部分视频采集音频采集iOS开发中,同音视频采集相关API都封装在AVFoundation中,导入该框架,即可实现音频、视频的同步采集采集步骤采集步骤文字描述导入框架同采集相关API在AVFoundation中,因此需要先导入框架创建捕捉会话(AVCaptureSession)会话:用于连接输入源、输出源输入源:摄像头、麦克风输出源:对应的视频、音频数据设置视频输入源、输出源输入源(AVCaptureDeviceInput):从摄像头输入(前置/后置)输出源(AVCaptureVideoDataOutput):可从代理方法中拿到数据将输入源、输出源
前言本期是Swift编辑组自主整理周报的第七期,每个模块已初步成型。各位读者如果有好的提议,欢迎在文末留言。欢迎投稿或推荐内容。目前计划每两周周一发布,欢迎志同道合的朋友一起加入周报整理。当你来到双水村以外的大世界,你的人生目标便不单单是一名庄稼人了。Swift社区陪你一起成长,一起创造更多可能!👊👊👊周报精选新闻和社区:【挑战上岛】适配实时活动和灵动岛提案:函数反向部署Swift论坛:围绕Swift6lock展开的讨论推荐博文:推荐500+款AppUI设计工具推荐:妙言话题讨论:如果您年龄超过35岁被裁员,再入职时能接受降薪吗?新闻和社区挑战上岛:适配实时活动和灵动岛Apple大中华区设计与
我的swiftmailer有问题,它没有向用户发送消息我将库提取到我网站的inc文件夹并创建了以下消息供swiftmailer发送:注意:如果您可以建议除SwiftMailer之外的其他解决方案,请发表评论。require_once'inc/lib/swift_required.php';//CreatetheTransport$transport=Swift_SmtpTransport::newInstance('mail.mywebsite.com',25)->setUsername('info@mywebsite.com')->setPassword('myPassword');