objc_getAssociatedObject always returns nil
Try the following (note the ampersands):
extension Timer {
private struct AssociatedKeys {
static var counterAddress = "counter_address"
}
public var repeatCounter: Int {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.counterAddress) as? Int ?? 0
}
set {
objc_setAssociatedObject(self,
&AssociatedKeys.counterAddress,
newValue,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
which always ensures that the same address is given as value for the key
parameter. If you use just a string value, its raw value may be different for each call of objc_getAssociatedObject
or objc_setAssociatedObject
.
You can check this with the following code:
func test(_ a: UnsafeRawPointer) {
print("\(a)")
}
let a = "abc"
test(a)
test(a)
test(a)
test(a)
This prints
0x00006040004427e0
0x00006040004427e0
0x0000608000052980
0x0000600000055c50
for instance, and different raw pointers are interpreted as different associated objects.
Swift objc_getAssociatedObject always nil
You cannot add associated objects to a Swift Array
(or any Swift value type). objc_setAssociatedObject()
and objc_getAssociatedObject()
are from the
Objective-C runtime, they expect an instance of NSObject
as first
argument.
Your code compiles and runs only because any Swift value is automatically
bridged to an object if necessary.
When you call
objc_setAssociatedObject(self, ...)
thenself
is bridged to a (temporary) instance ofNSArray
, and the association
is made on that object.Later, when
objc_getAssociatedObject(self, ...)
is called,
another (temporary) instance ofNSArray
is created, and that
has no associated object.
That's why you get nil
as the result.
objc_getAssociatedObject and Runtime attributes
Thanks to Tarun Tyagi for pointing out the correct fix.
@objc needs to be added to the property reference in the extension. Incorrectly marking the outside property results in a objc can only be used with members of classes, @objc protocols, and concrete extensions of classes error.
Working code is as follows:
import UIKit
var imgAttributeKey:String? = nil
extension UIImageView {
@objc var imgAttribute: String? {
get { return objc_getAssociatedObject(self, &imgAttributeKey) as? String }
set { objc_setAssociatedObject(self, &imgAttributeKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) }
}
}
The reason why this was needed it that this project was originally written in Swift 3 but starting from Swift 4, an explicit annotation @objc for dynamic Objective-C features (such as User Defined Runtime Attributes) is required.
New stored property always returns nil | iOS Swift
The fact is that objc_getAssociatedObject/objc_setAssociatedObject
assign a value to an object instance so it can be alive with this instance only. Since your custom property is not serialised by EventKit it's naturally that new obtained instances of events after the request have no associated objects.
objc_getAssociatedObject suddenly starts returning nil
You are using different keys when setting and getting the object.
Notice the &
when setting, but not when getting. You should use in both cases.
A good practice is to define the key like so:
static void* KEY_CONNECTION_MYCLASS = &KEY_CONNECTION_MYCLASS;
This way, your typo would not have mattered. But it's good to fix typos.
Is there a way to set associated objects in Swift?
Here is a simple but complete example derived from jckarter's answer.
It shows how to add a new property to an existing class. It does it by defining a computed property in an extension block. The computed property is stored as an associated object:
import ObjectiveC
// Declare a global var to produce a unique address as the assoc object handle
private var AssociatedObjectHandle: UInt8 = 0
extension MyClass {
var stringProperty:String {
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! String
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
EDIT:
If you need to support getting the value of an uninitialized property and to avoid getting the error unexpectedly found nil while unwrapping an Optional value
, you can modify the getter like this:
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""
}
set associated objects for literal value in Swift
NSData
is part of a class cluster, so your custom init method is not necessarily called,
e.g.
let d = NSMutableData()
does not use your init
method. The next problem is that your init method calls
itself recursively, therefore
let d = NSData()
crashes with a stack overflow. Note also that the Objective-C code relies on
undefined behaviour, because it replaces a method in a class extension.
So better remove your custom initialization, and change the getter to
return a default value if the associated object has not been set.
This can easily be achieved with an optional cast (as? Int
) and the
nil-coalescing operator (??
):
extension NSData {
var position: Int {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? Int ?? 0
}
set {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
}
Related Topics
How to Check Whether an Object Is Kind of a Dynamic Class Type in Swift
How to Call a Generic Swift Function When None of the Arguments Provides the Generic Type
Create Spotlight-Like Window in Swift 4
Firebase References Undeclared
Using @Fetchrequest(Entity: ) for Swiftui MACos App Crashes
Ibdesignable and Uitableviewcell
Is a Static Boolean a Reference Type in Swift
Non Exhaustive List When Handling Errors Inside a Class Function in Swift
Consuming a Soap Web Service with Swift
Realitykit - Load Another Scene from the Same Reality Composer Project
Swift: How to Hold Any Possible Instance of a Generic Type in a Variable
How to Get a Double Value Up to 2 Decimal Places
How to Implement Default Associated Values with Swift Enums
Closures Return Value (Previously Completionblock)
Swift: Move Uiimage Partly Along Path
Cannot Preview in This File -- Message Send Failure
Macos App Local Notification Not Showing When Testing with Xcode