如果实现 class 标记为私有,则不调用委托方法?
Delegate methods not called if implementing class is marked private?
这是一小段运行良好的 Swift 代码(其中 "fine" 被定义为 "Parsing!" 打印了一大堆以响应调用 class 方法Parse.parse
):
import Foundation
class Parse {
class func parse(stream: NSInputStream) {
return Parser().parse(stream)
}
class Parser: NSObject, NSXMLParserDelegate {
func parse(stream: NSInputStream) {
let XMLParser = NSXMLParser(stream: stream)
let delegate = XMLParserDelegate()
XMLParser.delegate = delegate
XMLParser.parse()
}
class XMLParserDelegate: NSObject, NSXMLParserDelegate {
func parser(
parser: NSXMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [NSObject : AnyObject])
{
NSLog("Parsing!")
}
}
}
}
当我尝试使用 Swift 的可见性功能时出现问题。特别是,我不想让 Parser
class 对其他文件可见(因为没有理由让它可见)。但是,如果我通过 private class Parser …
声明它,代码将停止工作! parser:didStartElement:namespaceURI:qualifiedName:attributes:
不再叫了!
这一切对我来说似乎很奇怪,不像它在任何其他语言中的工作方式。因此,我觉得以下两件事之一一定是真的:
Swift 的命名空间系统充其量也就是很奇怪。更明确地说,我觉得它坏了。
Swift 很好,我只是在做一些非常愚蠢的事情!如果真是这样,那就太好了!请告诉我它是什么!
感谢大家的帮助!
编辑:这是一个稍微精简的版本。和以前一样,代码工作正常,直到 Parser
class 被标记为 private
:
import Foundation
class Parse {
class func parse(stream: NSInputStream) {
return Parser().parse(stream)
}
}
class Parser: NSObject, NSXMLParserDelegate {
func parse(stream: NSInputStream) {
let XMLParser = NSXMLParser(stream: stream)
XMLParser.delegate = self
XMLParser.parse()
}
func parser(
parser: NSXMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [NSObject : AnyObject])
{
NSLog("Parsing!")
}
}
这不足为奇。 NSXMLParserDelegate
包括以下内容:
optional func parser(_ parser: NSXMLParser, didStartElement elementName: String, namespaceURI namespaceURI: String, qualifiedName qualifiedName: String, attributes attributeDict: [NSObject : AnyObject])
由于它是可选的,因此 NSXMLParser
中的某处必须有一个 doesRespondToSelector()
调用。如果底层 class 是私有的,函数会失败也就不足为奇了。 (考虑到它与动态 ObjC 调用的交互,如果它起作用也不会令人震惊;但是这两种方法都不应被认为是错误的,并且您描述的内容更符合您的要求;这就是这些方法是私有的。)
这里的正确答案是 XMLParserDelegate
需要 public 以及 NSXMLParserDelegate
的实现。 Parser
不一定是public,任何非协议方法也不一定是public。但是 NSXMLParser
如果你想让它调用它们,就需要能够看到它的委托方法。
更令人惊讶的是,这不是编译器错误。
编辑:虽然这并没有造成编译器错误仍然让我感到惊讶(而且我觉得那可能是一个错误), 关键发现是 private
表示私有。这意味着其他文件看不到该方法,因此 respondsToSelector()
将失败。为了以更简单的形式证明这一点:
main.swift
import Foundation
private class Impl : NSObject, P {
func run() {
println("Running")
}
}
let c = Container(p: Impl())
c.go()
more.swift
import Foundation
@objc internal protocol P: NSObjectProtocol {
optional func run()
}
// Change internal to private to see change
internal struct Container {
let p: P
func go() {
println(p.dynamicType) // Impl for internal, (Impl in ...) for private
if p.respondsToSelector(Selector("run")) {
p.run!() // if run is internal or public
} else {
println("Didn't implement") // if run is private
}
// Or the Swiftier way:
if let run = p.run {
run() // if run is internal or public
} else {
println("Didn't implement") // if run is private
}
}
}
要了解更多关于为什么这是真的细节,我们可以查看 p.dynamicType
。如果 p
是内部的,那么我们看到它的类型是 Impl
。如果它是私有的,我们看到它的类型是 (Impl in _9F9099C659B8A128A78BAA9A7C0E0368)
。使事物 private
使其类型和内部结构成为私有的。
私人只是比内部隐藏更多。它影响运行时,而不仅仅是编译时间。
我越想越明白为什么它不能给我们一个编译器错误。使用私有 class 实现内部协议是合法的。当我们将它作为参数传递给另一个访问区域,然后尝试动态地内省它时,它就会出现问题。答案是 "don't do that."
将来可能会发生变化。参见 https://devforums.apple.com/message/1073092#1073092。值得将其作为错误报告提出来,但我仍然不认为这是一个错误;这肯定是有意为之的行为。
根据 ,这似乎是 Swift 1.2 中引入的错误(或类似错误)。大概这种行为不是故意的,因为我不知道任何其他(合理的,静态类型的)语言将 class 标记为私有可以在运行时悄悄地破坏你的代码。我会跟进 Apple,看看他们对这一切的看法!
在 XCode 版本 6.1 (6A1052c) 之前已经存在类似的错误,它已在该版本中修复,现在在 XCode 版本 6.3 (6D532l) 中再次引入。
当时(6.1 之前)还有另一个错误,您必须通过隐式解包某些参数来手动更改 func parser(parser: NSXMLParser, didStartElement elementName: String...
方法的 NSXMLParserDelegate
签名以修复 Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
错误。
详细了解 here
完成后,如果 class 使用它被标记为私有,则不会调用此方法,因此它必须至少是内部的才能工作。
Apple 在 6.1 中修复了此方法签名,然后我能够将解析器 class 设为私有(see this),在 XCode 6.3 / Swift 1.2 之前效果很好.
现在,我不确定这是一个错误还是预期的行为,所以我想我会等待下一个正式版本,看看我应该如何处理我的 XML Parsing library。
顺便说一句,Xcode 6.3 Beta 4 中没有任何变化。
这是一小段运行良好的 Swift 代码(其中 "fine" 被定义为 "Parsing!" 打印了一大堆以响应调用 class 方法Parse.parse
):
import Foundation
class Parse {
class func parse(stream: NSInputStream) {
return Parser().parse(stream)
}
class Parser: NSObject, NSXMLParserDelegate {
func parse(stream: NSInputStream) {
let XMLParser = NSXMLParser(stream: stream)
let delegate = XMLParserDelegate()
XMLParser.delegate = delegate
XMLParser.parse()
}
class XMLParserDelegate: NSObject, NSXMLParserDelegate {
func parser(
parser: NSXMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [NSObject : AnyObject])
{
NSLog("Parsing!")
}
}
}
}
当我尝试使用 Swift 的可见性功能时出现问题。特别是,我不想让 Parser
class 对其他文件可见(因为没有理由让它可见)。但是,如果我通过 private class Parser …
声明它,代码将停止工作! parser:didStartElement:namespaceURI:qualifiedName:attributes:
不再叫了!
这一切对我来说似乎很奇怪,不像它在任何其他语言中的工作方式。因此,我觉得以下两件事之一一定是真的:
Swift 的命名空间系统充其量也就是很奇怪。更明确地说,我觉得它坏了。
Swift 很好,我只是在做一些非常愚蠢的事情!如果真是这样,那就太好了!请告诉我它是什么!
感谢大家的帮助!
编辑:这是一个稍微精简的版本。和以前一样,代码工作正常,直到 Parser
class 被标记为 private
:
import Foundation
class Parse {
class func parse(stream: NSInputStream) {
return Parser().parse(stream)
}
}
class Parser: NSObject, NSXMLParserDelegate {
func parse(stream: NSInputStream) {
let XMLParser = NSXMLParser(stream: stream)
XMLParser.delegate = self
XMLParser.parse()
}
func parser(
parser: NSXMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [NSObject : AnyObject])
{
NSLog("Parsing!")
}
}
这不足为奇。 NSXMLParserDelegate
包括以下内容:
optional func parser(_ parser: NSXMLParser, didStartElement elementName: String, namespaceURI namespaceURI: String, qualifiedName qualifiedName: String, attributes attributeDict: [NSObject : AnyObject])
由于它是可选的,因此 NSXMLParser
中的某处必须有一个 doesRespondToSelector()
调用。如果底层 class 是私有的,函数会失败也就不足为奇了。 (考虑到它与动态 ObjC 调用的交互,如果它起作用也不会令人震惊;但是这两种方法都不应被认为是错误的,并且您描述的内容更符合您的要求;这就是这些方法是私有的。)
这里的正确答案是 XMLParserDelegate
需要 public 以及 NSXMLParserDelegate
的实现。 Parser
不一定是public,任何非协议方法也不一定是public。但是 NSXMLParser
如果你想让它调用它们,就需要能够看到它的委托方法。
更令人惊讶的是,这不是编译器错误。
编辑:虽然这并没有造成编译器错误仍然让我感到惊讶(而且我觉得那可能是一个错误), 关键发现是 private
表示私有。这意味着其他文件看不到该方法,因此 respondsToSelector()
将失败。为了以更简单的形式证明这一点:
main.swift
import Foundation
private class Impl : NSObject, P {
func run() {
println("Running")
}
}
let c = Container(p: Impl())
c.go()
more.swift
import Foundation
@objc internal protocol P: NSObjectProtocol {
optional func run()
}
// Change internal to private to see change
internal struct Container {
let p: P
func go() {
println(p.dynamicType) // Impl for internal, (Impl in ...) for private
if p.respondsToSelector(Selector("run")) {
p.run!() // if run is internal or public
} else {
println("Didn't implement") // if run is private
}
// Or the Swiftier way:
if let run = p.run {
run() // if run is internal or public
} else {
println("Didn't implement") // if run is private
}
}
}
要了解更多关于为什么这是真的细节,我们可以查看 p.dynamicType
。如果 p
是内部的,那么我们看到它的类型是 Impl
。如果它是私有的,我们看到它的类型是 (Impl in _9F9099C659B8A128A78BAA9A7C0E0368)
。使事物 private
使其类型和内部结构成为私有的。
私人只是比内部隐藏更多。它影响运行时,而不仅仅是编译时间。
我越想越明白为什么它不能给我们一个编译器错误。使用私有 class 实现内部协议是合法的。当我们将它作为参数传递给另一个访问区域,然后尝试动态地内省它时,它就会出现问题。答案是 "don't do that."
将来可能会发生变化。参见 https://devforums.apple.com/message/1073092#1073092。值得将其作为错误报告提出来,但我仍然不认为这是一个错误;这肯定是有意为之的行为。
根据
在 XCode 版本 6.1 (6A1052c) 之前已经存在类似的错误,它已在该版本中修复,现在在 XCode 版本 6.3 (6D532l) 中再次引入。
当时(6.1 之前)还有另一个错误,您必须通过隐式解包某些参数来手动更改 func parser(parser: NSXMLParser, didStartElement elementName: String...
方法的 NSXMLParserDelegate
签名以修复 Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
错误。
详细了解 here
完成后,如果 class 使用它被标记为私有,则不会调用此方法,因此它必须至少是内部的才能工作。
Apple 在 6.1 中修复了此方法签名,然后我能够将解析器 class 设为私有(see this),在 XCode 6.3 / Swift 1.2 之前效果很好.
现在,我不确定这是一个错误还是预期的行为,所以我想我会等待下一个正式版本,看看我应该如何处理我的 XML Parsing library。
顺便说一句,Xcode 6.3 Beta 4 中没有任何变化。