Page 1 of 1

"AssociatedObject" key through different variable

Posted: Tue Oct 11, 2022 10:22 am
by mestnyi

Code: Select all

ImportC ""
  objc_setAssociatedObject( object,    ; Any
                            key.s,     ; UnsafeRawPointer,
                            value,     ; Any?,
                            policy )   ; objc_AssociationPolicy 
  
  objc_getAssociatedObject( object,    ; Any
                            key.s )    ; UnsafeRawPointer
EndImport

If OpenWindow(0, 0, 0, 300, 300, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
  Define key.s = "___data"
  Define key1.s = key ; "___data"
  
  objc_setAssociatedObject_( WindowID(0), key, 12345, 0 ) 
  Debug objc_getAssociatedObject_( WindowID(0), key1 ) 
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
EndIf     

Re: "AssociatedObject" key through different variable

Posted: Tue Oct 11, 2022 4:28 pm
by mestnyi
does anyone know why this is happening?

Re: "AssociatedObject" key through different variable

Posted: Tue Oct 11, 2022 6:23 pm
by fsw
Not sure why, but I found this:
https://nshipster.com/associated-objects/

And it says that the keys should be constants:
It is often recommended that they key be a static char—or better yet, the pointer to one. Basically, an arbitrary value that is guaranteed to be constant, unique, and scoped for use within getters and setters:
If the key is a variable and it is copied (as in your case) it is not unique anymore, is it?

The AssociatedObject functions uses the exact memory location (pointer) of the variable.

Code: Select all

  key.s,     ; UnsafeRawPointer,
The proof is in the pudding:
Using constants instead of variables works even when a constant is copied:

Code: Select all

#key = "___data"
#key2 = #key
and later using:

Code: Select all

  Debug objc_getAssociatedObject_( WindowID(0), #key ) 
  Debug objc_getAssociatedObject_( WindowID(0), #key2 ) 
  Debug objc_getAssociatedObject_( WindowID(0), "___data" ) 
shows the same results.

#key and #key2 seem to hold the pointer to "___data" because the outputs are the same.

Strangely enough, the second literal string "___data" in the objc_getAssociatedObject_ function call works as well, even though the memory location should differ from the first literal string of "___data" (while defining #key = "___data").
Maybe the PB compiler is smart enough and compares literal strings with already used literal strings and if it finds the same literal string it uses the pointer of the original literal string, instead of storing the same literal string twice.

Interesting stuff...

Re: "AssociatedObject" key through different variable

Posted: Wed Oct 12, 2022 8:10 pm
by mestnyi
I wanted to use like this.
It didn't work, but what surprises me is if I add this line "AllocateStructure(String)" anywhere, it starts working. :shock:
or when I assign "key" to a structure like this "Protected *key.String = @key"

Code: Select all

AllocateStructure(String) ;1 uncomment to see work - why??????????

DeclareModule Associated
  EnableExplicit
  Declare Get( object.i, key.s )
  Declare Remove( object.i, key.s )
  Declare Set( object.i, key.s, value.i )
EndDeclareModule

Module Associated
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    #OBJC_ASSOCIATION_ASSIGN = 0
    #OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1
    #OBJC_ASSOCIATION_COPY_NONATOMIC = 3
    #OBJC_ASSOCIATION_RETAIN = 769
    #OBJC_ASSOCIATION_COPY = 771
  CompilerEndIf
  
  Procedure Get( object.i, key.s )
    ;Protected *key.String = @key ;2 uncomment to see work
    
    CompilerSelect #PB_Compiler_OS 
      CompilerCase #PB_OS_MacOS
        ProcedureReturn objc_getAssociatedObject_( object, key ) 
        
      CompilerCase #PB_OS_Windows
        ProcedureReturn GetProp_( object, key )
        
    CompilerEndSelect                                                                  
  EndProcedure
  
  Procedure Remove( object.i, key.s )
    CompilerSelect #PB_Compiler_OS 
      CompilerCase #PB_OS_MacOS
        objc_setAssociatedObject_( object, key, #Null, #OBJC_ASSOCIATION_ASSIGN ) 
        ; objc_removeAssociatedObjects_(object )
        
      CompilerCase #PB_OS_Windows
        RemoveProp_( object, key )
        
    CompilerEndSelect                                                                    
  EndProcedure
  
  Procedure Set( object.i, key.s, value.i )
    CompilerSelect #PB_Compiler_OS 
      CompilerCase #PB_OS_MacOS
        objc_setAssociatedObject_( object, key, value, #OBJC_ASSOCIATION_ASSIGN ) 
        
      CompilerCase #PB_OS_Windows
        SetProp_(object, key, value )
        
    CompilerEndSelect                                                                   
  EndProcedure
EndModule

CompilerIf #PB_Compiler_IsMainFile
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    If OpenWindow(0, 0, 0, 300, 300, "Resize me !", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
      Define key.s = "___data"
      Define key1.s = key ; "___data"
      ;Define *key.String = @key1
      
;       objc_setAssociatedObject_( WindowID(0), key, 12345, 0 ) 
;       Debug objc_getAssociatedObject_( WindowID(0), key1 ) 
      
      Associated::Set( WindowID(0), "___data", 12345)
      Debug Associated::Get( WindowID(0), "___data")
      
      Repeat
        Event = WaitWindowEvent()
      Until Event = #PB_Event_CloseWindow
    EndIf 
  CompilerEndIf
CompilerEndIf
  

Re: "AssociatedObject" key through different variable

Posted: Thu Oct 13, 2022 6:38 pm
by fsw
Something is goofy...

This works as well even though it shouldn't:

Code: Select all

      Define key.s = "___data"
      Define key1.s = key ; "___data"

      Associated::Set( WindowID(0), "___data", 12345)
      Debug Associated::Get( WindowID(0), "___data")
      Debug Associated::Get( WindowID(0), key)
      Debug Associated::Get( WindowID(0), key1)
Weird!

Even more weird:

Code: Select all

      Define key.s = "___data"
      Define key1.s = key ; "___data"

      Associated::Set( WindowID(0), "___banana", 12345)
      Debug Associated::Get( WindowID(0), key)
      Debug Associated::Get( WindowID(0), key1)
      Debug Associated::Get( WindowID(0), "___d")
      Debug Associated::Get( WindowID(0), "___da")
      Debug Associated::Get( WindowID(0), "___dat")
      Debug Associated::Get( WindowID(0), "___data")
:shock:

Re: "AssociatedObject" key through different variable

Posted: Fri Oct 14, 2022 5:03 pm
by mestnyi
amazing things really happen :shock:
Well at least it works.

Code: Select all

DeclareModule Associated
  Declare Get( object.i, *key.String )
  Declare Remove( object.i, *key.String )
  Declare Set( object.i, *key.String, value.i )
EndDeclareModule

Module Associated
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    #OBJC_ASSOCIATION_ASSIGN = 0
    #OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1
    #OBJC_ASSOCIATION_COPY_NONATOMIC = 3
    #OBJC_ASSOCIATION_RETAIN = 769
    #OBJC_ASSOCIATION_COPY = 771
  CompilerEndIf
  
  Procedure Get( object.i, *key.String )
    CompilerSelect #PB_Compiler_OS 
      CompilerCase #PB_OS_MacOS
        ProcedureReturn objc_getAssociatedObject_( object, *key\s ) 
        
      CompilerCase #PB_OS_Windows
        ProcedureReturn GetProp_( object, *key\s )
        
    CompilerEndSelect                                                                  
  EndProcedure
  
  Procedure Remove( object.i, *key.String )
    CompilerSelect #PB_Compiler_OS 
      CompilerCase #PB_OS_MacOS
        objc_setAssociatedObject_( object, *key\s, #Null, #OBJC_ASSOCIATION_ASSIGN ) 
        ; objc_removeAssociatedObjects_(object )
        
      CompilerCase #PB_OS_Windows
        RemoveProp_( object, *key\s )
        
    CompilerEndSelect                                                                    
  EndProcedure
  
  Procedure Set( object.i, *key.String, value.i )
    CompilerSelect #PB_Compiler_OS 
      CompilerCase #PB_OS_MacOS
        objc_setAssociatedObject_( object, *key\s, value, #OBJC_ASSOCIATION_ASSIGN ) 
        
      CompilerCase #PB_OS_Windows
        SetProp_(object, *key\s, value )
        
    CompilerEndSelect                                                                   
  EndProcedure
EndModule

CompilerIf #PB_Compiler_IsMainFile
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    If OpenWindow(0, 0, 0, 300, 300, "Resize me !", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
;       Define key.s = "___data"
;       Define key1.s = key ; "___data"
;       
; ;       objc_setAssociatedObject_( WindowID(0), key, 12345, 0 ) 
; ;       Debug objc_getAssociatedObject_( WindowID(0), key1 ) 
;       
;       Associated::Set( WindowID(0), @"___data", 12345)
;       Debug Associated::Get( WindowID(0), @"___data")
;       Associated::Remove( WindowID(0), @"___data")
;       Debug Associated::Get( WindowID(0), @"___data")
      
      Define key.s = "___data"
      Define key1.s = key ; "___data"

      Associated::Set( WindowID(0), @"___banana", 12345)
      Debug Associated::Get( WindowID(0), @key)
      Debug Associated::Get( WindowID(0), @key1)
      Debug Associated::Get( WindowID(0), @"___d")
      Debug Associated::Get( WindowID(0), @"___da")
      Debug Associated::Get( WindowID(0), @"___dat")
      Debug Associated::Get( WindowID(0), @"___data")
      Debug Associated::Get( WindowID(0), @"___banana")
      
      Repeat
        Event = WaitWindowEvent()
      Until Event = #PB_Event_CloseWindow
    EndIf 
  CompilerEndIf
CompilerEndIf

Re: "AssociatedObject" key through different variable

Posted: Fri Oct 14, 2022 5:11 pm
by mestnyi
no, that doesn't work either.
it turns out the key should not be longer than 4 characters :shock:

Code: Select all

      Associated::Set( WindowID(0), @"___data", 12345)
      Associated::Set( WindowID(0), @"___data2", 54321)
      Debug Associated::Get( WindowID(0), @"___data")
      Debug Associated::Get( WindowID(0), @"___data2")

Re: "AssociatedObject" key through different variable

Posted: Fri Oct 14, 2022 5:28 pm
by mestnyi
he did not need a line :shock:

Code: Select all

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    If OpenWindow(0, 0, 0, 300, 300, "Resize me !", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
      Define key3 = 1234566788
      Define key4 = key3
      
      objc_setAssociatedObject_( WindowID(0), 123456789, 12345, 0 ) 
      Debug objc_getAssociatedObject_( WindowID(0), 123456789 ) 
      
      objc_setAssociatedObject_( WindowID(0), key3, 12345, 0 ) 
      Debug objc_getAssociatedObject_( WindowID(0), key4 ) 
      
      Repeat
        Event = WaitWindowEvent()
      Until Event = #PB_Event_CloseWindow
    EndIf 
  CompilerEndIf

Re: "AssociatedObject" key through different variable

Posted: Sat Oct 15, 2022 10:27 am
by mk-soft
Why not use maps ...

Update
- Bugfix

Code: Select all

DeclareModule Associated
  Declare Get( object.i, key.s )
  Declare Set( object.i, key.s, value.i )
  Declare Remove( object.i, key.s )
  Declare Release( object.i )
EndDeclareModule

Module Associated
  
  Structure udtAssociatedObject
    Map key.i()
  EndStructure
  
  Global NewMap mapAssociatedObject.udtAssociatedObject()
  
  Procedure Get( object.i, key.s )
    If FindMapElement(mapAssociatedObject(), Str(object))
      If FindMapElement(mapAssociatedObject()\key(), key)
        ProcedureReturn mapAssociatedObject()\key()
      EndIf
    EndIf
  EndProcedure
  
  Procedure Set( object.i, key.s, value.i )
    If Not FindMapElement(mapAssociatedObject(), Str(object))
      AddMapElement(mapAssociatedObject(), Str( object))
    EndIf
    If AddMapElement(mapAssociatedObject()\key(), key)
      mapAssociatedObject()\key() = value
      ProcedureReturn #True
    EndIf
    ProcedureReturn #False
  EndProcedure
  
  Procedure Remove( object.i, key.s )
    If FindMapElement(mapAssociatedObject(), Str(object))
      If FindMapElement(mapAssociatedObject()\key(), key)
        DeleteMapElement(mapAssociatedObject()\key())
      EndIf
    EndIf
  EndProcedure
  
  Procedure Release( object.i )
    If FindMapElement(mapAssociatedObject(), Str(object))
      DeleteMapElement(mapAssociatedObject())
    EndIf
  EndProcedure
  
  
EndModule

CompilerIf #PB_Compiler_IsMainFile
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
    If OpenWindow(0, 0, 0, 300, 300, "Resize me !", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
      
      Define key.s = "___data"
      Define key1.s = key ; "___data"

      Associated::Set( WindowID(0), "___banana", 12345)
      Associated::Set( WindowID(0), key1, 100)
      Debug Associated::Get( WindowID(0), key)
      Debug Associated::Get( WindowID(0), key1)
      Debug Associated::Get( WindowID(0), "___d")
      Debug Associated::Get( WindowID(0), "___da")
      Debug Associated::Get( WindowID(0), "___dat")
      Debug Associated::Get( WindowID(0), "___data")
      Debug Associated::Get( WindowID(0), "___banana")
      
      Repeat
        Event = WaitWindowEvent()
      Until Event = #PB_Event_CloseWindow
    EndIf 
  CompilerEndIf
CompilerEndIf

Re: "AssociatedObject" key through different variable

Posted: Sun Oct 16, 2022 7:24 pm
by mestnyi
mk-soft wrote: Sat Oct 15, 2022 10:27 am Why not use maps ...
I thought it would be better and more reliable :(
And the memory will be less.

Re: "AssociatedObject" key through different variable

Posted: Sun Oct 16, 2022 9:54 pm
by fsw
@mk-soft:
This is the output on my M1 Mac:
100
100
0
0
0
100
0
but it should be:
100
100
0
0
0
100
12345
@mestnyi
Further testing with your first code shows that it works perfectly as long as real constants or literal strings are used.

Re: "AssociatedObject" key through different variable

Posted: Sun Oct 16, 2022 11:27 pm
by mk-soft