[AHK v2] Hotkey command break circular reference? Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

[AHK v2] Hotkey command break circular reference?

17 Nov 2018, 06:48

so i have this piece of code here:

Code: Select all

class MyHotkey
{
	__New(hkName, fn) {
		this.hkName := hkName
		Hotkey(this.hkName, () => this.doHotkey(fn)) ; im assuming this creates some unreleased references
	}

	__Delete() {
		MsgBox("dtor called")
		Hotkey(this.hkName, "Off")
	}

	doHotkey(fn) {
		fn.Call()
		Sleep(500)
		ToolTip("did hotkey action`n" A_TickCount)
	}
}

showTimeNow := () => ToolTip("time is:`n" A_Now)
myHk := new MyHotkey("q", showTimeNow)

w::myHk := "" ; doesnt call dtor
e::Hotkey("q", "Off") ; this works, but is explicit, external
r::Hotkey(myHk.hkName, "Off") ; same as above

Esc::ExitApp(0)
it defines a hotkey, calls the passed functor and wraps it with additional functionality.
now, what i want to achieve is this: when myHk is blanked out or reassigned, the hotkey should be automatically deactivated via Hotkey(hotkey_name, "Off)
so i figured, i could put this in the destructor, but the destructor never gets called.
if i comment out the hotkey defining function, the destructor will get called, so reckon the hotkey defining function is creating some references and is keeping them around

question is whether thats the case, and if what i wanna do is at all feasible.
as a workaround i could deactivate the hotkey by calling Hotkey(hotkey_name, "Off") myself, but id prefer not to
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [AHK v2] Hotkey command break circular reference?  Topic is solved

17 Nov 2018, 07:01

Code: Select all

Hotkey(this.hkName, () => this.doHotkey(fn)) ; im assuming this creates some unreleased references
Yes, because in () => this.doHotkey(fn), this is a free variable. You can do Hotkey(this.hkName, this.doHotkey.bind('', fn)). But then this is not available in doHotkey(), it doesn't matter in your current implementation, but is probably undesireable in general. Another possibility is to use a proxy object, example here. You can also bind the address of the object, as long as you are careful not to call object(ptr) when ptr is invalid.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [AHK v2] Hotkey command break circular reference?

17 Nov 2018, 10:29

Helgef wrote:
17 Nov 2018, 07:01
You can also bind the address of the object, as long as you are careful not to call object(ptr) when ptr is invalid.
i ended up doing it this way, since some methods rely on instance vars. thanks
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [AHK v2] Hotkey command break circular reference?

17 Nov 2018, 10:41

As a side note, to actually free the object in your original code, you need to replace the hotkey, turning it off is not sufficent. Example, e::Hotkey("q", ()=>, "Off"), myHk := "" ; calls dtor. Edit: As far as I can tell, there is no documented way to release the object.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [AHK v2] Hotkey command break circular reference?

08 Feb 2019, 17:23

it just occurred to me, if the doHotkey() method doesnt need to reference any instance vars, u can just bind against the class itself.
i was hoping i would have been able to do the lambda, as seen in 2), buuut it doesnt work like that

Code: Select all

class MyHotkey
{
	__New(hkName, fn) {
		this.hkName := hkName
		Hotkey(this.hkName, ObjBindMethod(%this.__Class%, "doHotkey", fn))   ; this will bind the class itself and the class' doHotkey() method

		; Hotkey(this.hkName, () => %this.__Class%.doHotkey(fn))             ; this, oth, seems to bind the instantiated class instance, so ref count is incremented...
		
		; Hotkey(this.hkName, () => MyHotkey.doHotkey(fn))                   ; this will achieve the same as 1), except u gotta explicitly write the class name in

		; className := this.__Class                                          ; u can game it, if u first store the classname in a plain var 
		; Hotkey(this.hkName, () => %className%.doHotkey(fn))                ; then the lambda no longer stores a reference to the instantiated object
	}

	__Delete() {
		MsgBox("dtor called")
		Hotkey(this.hkName, "Off") ; replace the time-tooltip lambda with a dummy lambda
	}

	doHotkey(fn) {
		fn.Call()
		Sleep(500)
		ToolTip("did hotkey action`n" A_TickCount)
	}
}

showTimeNow := () => ToolTip("time is:`n" A_Now)
myHk := new MyHotkey("q", showTimeNow)

w::myHk := ""
Esc::ExitApp()
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [AHK v2] Hotkey command break circular reference?

08 Feb 2019, 17:30

the fat arrow function becomes a closure due to this in %this.__Class% (and fn).

Edit 3, alternative: hotkey this.hkName, this.doHotkey.bind(MyHotkey, fn) and if you do not need MyHotkey in doHotkey, you can bind eg "" instead.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: [AHK v2] Hotkey command break circular reference?

08 Feb 2019, 17:58

yeah, i was hoping %this.__Class% would get evaluated and resolve to MyHotkey, and then MyHotkey.doHotkey(fn) would be passed to the lambda, but instead the whole %this.__Class%.doHotkey(fn) gets passed to the lambda, and then gets evaluated inside of it, which is no good since this's refcount will have been incremented by then

lol idk why i posted this, since u had already suggested Hotkey(this.hkName, this.doHotkey.bind('', fn)) earlier. its superior to ObjBindMethod(), yes, in that it can skip binding anything. well, at least all possibilities are exhausted now

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: gongnl, wjt936826577 and 49 guests