Reputation: 854
I'm using Xcode 10.0 with swift 4.2 to learn about Key Value Coding from "Cocoa programming for OSX"
I'm asked to create a simple class, which is a subclass of NSObject. The codes below:
import Cocoa
class Student: NSObject {
var name: String = ""
var gradeLevel: Int = 0
}
let student1 = Student()
student1.setValue("Benny", forKeyPath: "name")
student1.setValue("Benny", forKeyPath: "name")
Generates the following error message:
Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
I've looked online and seem some issues regarding KVC such as : https://bugs.swift.org/browse/SR-5139
What am I doing wrong? The book was published in 2015.
Upvotes: 6
Views: 4544
Reputation: 309
Simple Example of KVC
Here we access the name property of SimpleClass directly through the instance.
class SimpleClass {
var name: String
init(name: String) {
self.name = name
}
}
let instance = SimpleClass(name: "Mike")
let name = instance.name
print("name: \(name)")
But in KVC Here we access the name property of the class through forKey.
class KVCClass: NSObject {
@objc dynamic var name: String
init(name: String) {
self.name = name
}
}
let instance = KVCClass(name: "Mike")
if let name = instance.value(forKey: "name") as? String {
print("name: \(name)")
}
Upvotes: 1
Reputation: 285150
In Swift 4 exposing code to the Objective-C runtime is no longer inferred for performance reasons.
To avoid the crash you have to add the @objc
attribute explicitly.
@objc var name: String = ""
But from the strong type perspective of Swift there are two better ways to get values with KVC:
The #keyPath
directive which uses the ObjC runtime, too, but checks the key path at compile time
let keyPath = #keyPath(Student.name)
student1.setValue("Benny", forKeyPath: keyPath)
In this case you get a very descriptive compiler warning
Argument of '#keyPath' refers to property 'name' in 'Student' that depends on '@objc' inference deprecated in Swift 4
The (recommended) native way: Swift 4+ provides its own KVC pattern where subclassing NSObject
is not required.
Key paths are indicated by a leading backslash followed by the type and the property (or properties):
class Student {
var name: String = ""
var gradeLevel: Int = 0
}
let student1 = Student()
student1[keyPath: \Student.name] = "Benny"
Upvotes: 12