如果实现类被标记为私有,则不调用委托方法?

时间:2015-02-12 20:27:55

标签: ios swift delegates private

这里是一段很简单的Swift代码片段,工作正常(其中"罚款"定义为"解析!"正在打印一大堆以响应调用该类方法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类对其他文件可见(因为没有理由让它可见)。如果我通过private class Parser …声明它,那么代码就不再起作用了! parser:didStartElement:namespaceURI:qualifiedName:attributes:不再被召唤!

这一切对我来说都很奇怪,不像它在任何其他语言中如何运作。因此,我觉得以下两件事之一必须是真的:

  1. Swift的命名空间系统充其量是奇怪的。更明显的是,它似乎对我不利。

  2. 斯威夫特很好,我只是在做一些非常愚蠢的事情!如果是这样,那就太好了!请让我知道它是什么!

  3. 感谢大家的帮助!


    修改:这是一个略微修剪的版本。和以前一样,代码正常工作,直到Parser类标记为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!")
      }
    }
    

3 个答案:

答案 0 :(得分:2)

这应该不足为奇。 NSXMLParserDelegate包括以下内容:

optional func parser(_ parser: NSXMLParser, didStartElement elementName: String, namespaceURI namespaceURI: String, qualifiedName qualifiedName: String, attributes attributeDict: [NSObject : AnyObject])

由于它是可选的,因此NSXMLParser中必须有doesRespondToSelector()次呼叫。如果底层类是私有的,那么函数会失败也就不足为奇了。 (鉴于它与动态ObjC调用的交互,如果它起作用也不会令人震惊;但是这两种方法都不应该被认为是破坏的,你所描述的更符合你所要求的;这些方法是私有的。)< / p>

这里正确的答案是XMLParserDelegate需要公开NSXMLParserDelegateParser不必公开,任何非协议方法都不需要公开。但是NSXMLParser需要能够看到它的委托方法,如果你想让它们调用它们。

更令人惊讶的是,这不是编译器错误。


编辑:虽然我仍然感到惊讶,这不会产生编译器错误(我觉得 可能是一个错误),关键发现是{ {1}}表示私密。这意味着其他文件无法看到该方法,因此private将失败。为了以更简单的形式证明这一点:

main.swift

respondsToSelector()

more.swift

import Foundation
private class Impl : NSObject, P {
    func run() {
        println("Running")
    }
}
let c = Container(p: Impl())
c.go()

要了解有关其原因的详细信息,我们可以查看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)使其类型和内部结构变得私密。

私人隐藏的远不止内部。它会影响运行时,而不仅仅是编译时间。

我想的越多,我就越明白它为什么不能给我们编译错误。使用私有类实现内部协议是合法的。它横向移动的地方是我们将它作为参数传递给另一个访问区域然后尝试动态地内省它。答案是“不要这样做。”

将来可能会发生变化。见https://devforums.apple.com/message/1073092#1073092。它值得提出作为bug报告,但我仍然不认为它是一个bug;它肯定是有意的行为。

答案 1 :(得分:0)

根据the investigation done by MartinR,这似乎是Swift 1.2中引入的一个错误(或类似的错误)。据推测,这种行为并非故意,因为我不知道任何其他(合理的,静态类型)语言,将类标记为私有可以在运行时静默地破坏您的代码。我将跟进Apple,看看他们对这一切的看法!

答案 2 :(得分:0)

在XCode版本6.1(6A1052c)之前已经存在类似的错误,它已在该版本中修复,现在再次在XCode版本6.3(6D532l)中引入。

当时(在6.1之前)还有另一个错误,你必须手动更改NSXMLParserDelegate方法的func parser(parser: NSXMLParser, didStartElement elementName: String...签名,方法是隐式解包一些参数以修复Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)错误。 详细了解here

在您完成此操作后,如果使用它的类被标记为私有,则不会调用此方法,因此必须至少在内部才能正常工作。

Apple在6.1中修复了这个方法签名,然后我能够使解析器类私有(see this)在XCode 6.3 / Swift 1.2之前运行良好。

现在,我不确定这是一个错误或预期的行为,所以我想我会等到下一个正式版本,看看我应该怎么做我的{{3} }。

顺便说一句,Xcode 6.3 Beta 4中没有任何改变。