Swift Objc_Getassociatedobject Always Nil

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, ...) then self
    is bridged to a (temporary) instance of NSArray, and the association
    is made on that object.

  • Later, when objc_getAssociatedObject(self, ...) is called,
    another (temporary) instance of NSArray 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



Leave a reply



Submit