p-x9 / AssociatedObject

🔗 Swift Macro for allowing variable declarations even in class extensions
MIT License
126 stars 3 forks source link

Improve key implementation #28

Closed p-x9 closed 7 months ago

p-x9 commented 7 months ago

Within a Protocol Extension, stored property could not be defined, and an error occurred in the definition of the AssociatedObject's Key.

Therefore, I modified the implementation of Key to use Selector.

related to: https://github.com/p-x9/AssociatedObject/pull/24

mlch911 commented 7 months ago

We can add a assert in the setter to check is there anything wrong.

var string: String? {
    get {
        objc_getAssociatedObject(
            self,
            Self.__associated_stringKey
        ) as? String?
        ?? nil
    }
    set {
        objc_setAssociatedObject(
            self,
            Self.__associated_stringKey,
            newValue,
            .OBJC_ASSOCIATION_ASSIGN
        )
        assert(string == newValue)    // <-- Add this.
    }
}

static var __associated_stringKey: UnsafeRawPointer {
    unsafeBitCast(
        NSSelectorFromString("__macro_local_6stringfMu_"),
        to: UnsafeRawPointer.self
    )
}
p-x9 commented 7 months ago

@mlch911

I think it is a good idea. However, I think it would be difficult to check that the type is Equatable using only syntax.

mlch911 commented 7 months ago

Maybe this.

set {
        objc_setAssociatedObject(
            self,
            Self.__associated_stringKey,
            newValue,
            .OBJC_ASSOCIATION_ASSIGN
        )
    if type.of(string).confirm(to: Equitable.self) {
        assert(string == newValue)
    }
}
p-x9 commented 7 months ago

@mlch911 This function may exist only in NSObject.

p-x9 commented 7 months ago

I have made some changes to the way keys are generated in order to support future platforms, such as Linux, where the Objective-C runtime is not available.

Instead of using Selector, function pointers are used as keys.

static var __associated_boolKey: UnsafeRawPointer {
    let f: @convention(c) () -> Void = {}
    return unsafeBitCast(f, to: UnsafeRawPointer.self)
}
mlch911 commented 7 months ago

I have made some changes to the way keys are generated in order to support future platforms, such as Linux, where the Objective-C runtime is not available.

Instead of using Selector, function pointers are used as keys.

static var __associated_boolKey: UnsafeRawPointer {
    let f: @convention(c) () -> Void = {}
    return unsafeBitCast(f, to: UnsafeRawPointer.self)
}

Would this return the same pointer every time? Shouldn't this be like this?

static let __associated_boolKey: UnsafeRawPointer = {
    let f: @convention(c) () -> Void = {}
    return unsafeBitCast(f, to: UnsafeRawPointer.self)
}()
p-x9 commented 7 months ago

Closure functions are not defined dynamically, but statically, just like any other function definition. Therefore, it should return the same pointer value change each time.

And for cases where it is not possible to define a property of a stored type, such as a protocol extension, I want to define it as a calculated type property as much as possible.