我必须通过字符串名称表示将值设置为属性。
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 dynamic var x:String =""
注意:Swift结构不能用Objective-C表示 @objc dynamic var 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(如答案中所述)
答案 0 :(得分:3)
<强>文档强>
没有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运行时环境的系统:
从NSObject或其子类之一继承的Swift对象 默认情况下,键值编码符合其属性。而在 Objective-C,属性的访问者和实例变量必须遵循 某些模式,Swift中的标准属性声明 自动保证这一点。另一方面,很多 协议的功能要么不相关,要么处理得更好 使用本机Swift结构或不存在的技术 Objective-C的。例如,因为所有Swift属性都是对象, 你从不运用默认实现的特殊处理 非对象属性。
可以从Objective-C调用的Swift API必须可用 通过动态调度。但是,动态的可用性 dispatch不会阻止Swift编译器选择更多 从Swift调用这些API时的高效调度方法 代码。
您可以将@objc属性与动态修改器一起使用 通过动态调度对成员的访问权限 Objective-C运行时。要求这种动态调度很少 必要。但是,在使用键值这样的API时是必要的 观察或方法中的method_exchangeImplementations函数 Objective-C运行时,它动态地替换了一个实现 运行时的方法。
使用动态修饰符标记的声明也必须是显式的 除非@objc属性,否则标有@objc属性 由声明的上下文隐式添加。有关的信息 隐式添加@objc属性时,请参阅声明 Swift编程语言中的属性(Swift 4)。
元素也必须声明为动态才能与KVO兼容(对于KVC,继承自NSObject
就足够了):
@objc dynamic var x:String = ""
如果String
无效,请尝试使用NSString
。
如果两者都没有帮助,这似乎是一个特定于Linux的问题,它似乎不支持KVC / KVO机制(这也是可以理解的)。
P.S。提供代码后,您的问题也会在Mac上的Xcode中重现。