只有从NSObject继承的类才能声明为@objc

时间:2017-10-20 12:52:32

标签: swift linux

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

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(如答案中所述)

1 个答案:

答案 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运行时环境的系统:

Key-Value Coding with Swift

  

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

另外:Requiring Dynamic Dispatch

  

可以从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中重现。