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.
According to Apple: Key-value coding is a mechanism for accessing an object's properties indirectly, using strings to identify properties, rather than through invocation of an accessor method or accessing them directly through instance variables.
Structures and enumerations don't support inheritance, which means the runtime doesn't have to figure out which implementation it needs to use. So to use KVC & KVO in swift, for the properties we want to observe in KVO we need to declare them with @objc dynamic keyword.
KVC is a mechanism that allows you to access properties without directly invoking accessor methods or instance variables. KVC uses a string, referred to as the key, to identify an attribute or property for a given object. Ruby developers may find KVC similar to the send method and other metaprogramming techniques.
KVO and KVC or Key-Value Observing and Key-Value Coding are mechanisms originally built and provided by Objective-C that allows us to locate and interact with the underlying properties of a class that inherits NSObject at runtime.
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"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With