【问题标题】:Only classes that inherit from NSObject can be declared @objc只有继承自 NSObject 的类才能声明为@objc
【发布时间】:2017-10-20 12:52:31
【问题描述】:

我必须通过字符串名称表示来设置属性的值。

import Foundation

@objc class A:NSObject {
    var x:String = ""
}

var a = A()
a.x = "ddd"

print(a.x)

a.setValue("zzz", forKey:"x")

print(a.x)

在编译过程中出现奇怪的错误:

main.swift:4:2: error: only classes that inherit from NSObject can be declared @objc

@objc class A:NSObject {

~^~~~~

main.swift:13:1: error: value of type 'A' has no member 'setValue'

a.setValue("zzz", forKey:"x")

^ ~~~~~~~~

有人知道发生了什么吗?

PS:可在 Swift 4.0 和 3.1.1(Ubuntu 16.04.3 LTS)上重现

已编辑:

import Foundation

@objc class A:NSObject {
   @objc dynamic  var x:String = ""
}

var a = A()
a.x = "ddd"

print(a.x)

a.setValue("zzz", forKeyPath:"x")

print(a.x)

输出:

错误:只有继承自 NSObject 的类才能声明为@objc @objc 类 A:NSObject {

错误:属性不能被标记为@objc,因为它的类型不能在Objective-C中表示 @objc 动态变量 x:String = ""

注意:Swift 结构不能在 Objective-C 中表示 @objc 动态变量 x:String = ""

错误:“A”类型的值没有成员“setValue” a.setValue("zzz", forKeyPath:"x")

编辑 2: 就像“c-style”一样尝试:

func set<T>(_ val:T, forKey key:String) {
    print("SET:\(self) \(key) to \(val)")
    let ivar: Ivar = class_getInstanceVariable(type(of: self), key)!
    let pointerToInstanceField:UnsafeMutableRawPointer = Unmanaged.passRetained(self).toOpaque().advanced(by: ivar_getOffset(ivar))
    let pointer = pointerToInstanceField.assumingMemoryBound(to: T.self)
    pointer.pointee = val
}

它运行良好,但在递归调用中会导致错误访问。可能有一些保留/释放问题。会挖北斗。也不适用于 Linux(如答案中所述)

【问题讨论】:

  • 这是一个误导性错误(您可能很想提交一个错误)。问题很简单,Linux 上没有 Swift 的 Obj-C 运行时。你不能标记 @objc,也不能使用 KVC(考虑使用 Swift 键路径)。
  • @Hamish,谢谢你的建议。你能指点我一些关于 Swift 关键路径的文档吗?不能谷歌任何关于。它还需要 (at)objc 后缀。我说的对吗?
  • @jww,我的问题不是重复你的建议
  • @NickZaporozhchenko Swift 关键路径本身不需要 Obj-C 暴露;您可以在 the evolution proposal 上阅读更多关于它们的信息(您还可以通过简单搜索“Swift 4 keypath”找到许多在线资源)。

标签: swift linux


【解决方案1】:

文档

没有 Objective-C 运行时的 Swift:Linux 上的 Swift 不依赖于 在 Objective-C 运行时也不包括它。在设计 Swift 时 为了在 Objective-C 存在时与它紧密互操作,它是 还设计用于在 Objective-C 运行时的环境中工作 不存在。

https://swift.org/blog/swift-linux-port/

这很清楚,只要它声明:

“A”类型的值没有成员“setValue”

它基本上说明了下面没有KVC机制。 setValue 方法来自 Objective-C 运行时,在 Linux 上不存在。因此,这是不行的,你想要完成的事情根本不可能。

除此之外,以下规则适用于具有 Obj-C 运行时环境的系统:


Key-Value Coding with Swift

从 NSObject 或其子类之一继承的 Swift 对象是 默认情况下符合其属性的键值编码。而在 Objective-C,属性的访问器和实例变量必须遵循 某些模式,Swift 中的标准属性声明 自动保证这一点。另一方面,许多 协议的特性要么不相关,要么处理得更好 使用 Swift 中不存在的原生 Swift 结构或技术 目标-C。例如,因为所有 Swift 属性都是对象, 你永远不会使用默认实现的特殊处理 非对象属性。

还有:Requiring Dynamic Dispatch

可从 Objective-C 调用的 Swift API 必须可用 通过动态调度。然而,动态的可用性 dispatch 不会阻止 Swift 编译器选择更多 从 Swift 调用这些 API 时的高效调度方法 代码。

您使用 @objc 属性和动态修饰符来要求 对成员的访问通过 Objective-C 运行时。很少需要这种动态调度 必要的。但是,在使用 key-value 之类的 API 时是必要的 观察或 method_exchangeImplementations 函数 Objective-C 运行时,它动态替换一个 运行时的方法。

用动态修饰符标记的声明也必须显式 用@objc 属性标记,除非@objc 属性是 由声明的上下文隐式添加。有关信息 当隐式添加 @objc 属性时,请参阅声明 Swift 编程语言 (Swift 4) 中的属性。


元素还必须声明为动态的,以便与 KVO 兼容(对于 KVC,从 NSObject 继承就足够了):

@objc dynamic var x:String = ""

如果String 不起作用,请尝试使用NSString

如果两者都没有帮助,这似乎是 Linux 特有的问题,它似乎不支持 KVC/KVO 机制(这也是可以理解的)。

附:使用提供的代码,您的问题也可以在 Mac 上的 Xcode 中重现。

【讨论】:

  • 刚试过 - 没有成功。还增加了新的错误:1:属性不能被标记为@objc,因为它的类型不能在Objective-C中表示; 2: Swift 结构不能在 Objective-C 中表示
  • 尝试将其定义为NSString
  • 除此之外,这有点令人惊讶。 The String type bridges with the Objective-C class NSString and offers interoperability with C functions that works with strings.
  • @NickZaporozhchenko 请注意,KVC 不需要dynamic;仅 KVO。
  • @Hexfire @objc 仍然是 KVC 所必需的 :) 它需要暴露给 Obj-C 运行时(以便 setValue(_:forKeyPath:) 可以看到),但 Swift 本身没有不需要使用消息分发来调用属性的访问器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-20
相关资源
最近更新 更多