ios - 如果实现类标记为私有(private),则不调用委托(delegate)方法?

标签 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. Swift 很好,我只是在做一些非常愚蠢的事情!如果真是这样,那就太好了!请告诉我它是什么!

谢谢大家的帮助!


编辑:这是一个稍微精简的版本。和以前一样,代码工作正常,直到 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!")
  }
}

最佳答案

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

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

因为它是可选的,所以 NSXMLParser 中的某处必须是 doesRespondToSelector() 调用。如果底层类是私有(private)的,函数会失败也就不足为奇了。 (考虑到它与动态 ObjC 调用的交互,如果它有效也不会令人震惊;但是这两种方法都不应被认为是错误的,并且您描述的内容更符合您的要求;这就是这些方法是私有(private)的。)

这里的正确答案是 XMLParserDelegate 需要与其实现的 NSXMLParserDelegate 一起公开。 Parser 不必公开,任何非协议(protocol)方法都不需要公开。但是 NSXMLParser 需要能够看到它的委托(delegate)方法才能调用它们。

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


编辑:尽管这并没有造成编译器错误(而且我觉得可能是一个错误)仍然让我感到惊讶,但关键的发现是 private 表示私有(private)。这意味着其他文件看不到该方法,因此 respondsToSelector() 将失败。为了以更简单的形式证明这一点:

主.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。如果它是私有(private)的,我们看到它的类型是 (Impl in _9F9099C659B8A128A78BAA9A7C0E0368)。将事物设为 private 会使它们的类型和内部结构成为私有(private)的。

私有(private)只是比内部隐藏更多。它会影响运行时,而不仅仅是编译时间。

我越想越明白为什么它不能给我们一个编译器错误。用私有(private)类实现内部协议(protocol)是合法的。当我们将它作为参数传递给另一个访问区域,然后尝试动态地内省(introspection)它时,它就会出现问题。答案是“不要那样做”。

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

关于ios - 如果实现类标记为私有(private),则不调用委托(delegate)方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28487142/

相关文章:

ios - 无法保持选择表行

ios - Objective-C - 将指针旋转一圈

ios - 核心数据: Illegal attempt to establish a relationship 'statusmedia' between objects in different contexts

iOS swift : Array of Objects conforming to a Protocol: How to check if array contains

user-controls - 在 Windows 窗体用户控件中单击子项时触发父级单击事件

ios - 6+ 的 UITableViewCell 中的不同缩进值?

ios - 如何将 View 样式设置为 16 :9 in React Native? 之类的比例

ios - 使用 API 响应 Swift 填充表

Swift Firebase UICollectionView 刷新导致重复的缩略图

ios - 自定义委托(delegate)在 iOS 中不起作用