[Cuis-dev] How to use Events?

Juan Vuletich juan at jvuletich.org
Mon Sep 16 07:02:15 PDT 2019


Hi Philip,

(below)
On 9/9/2019 10:04 AM, Juan Vuletich via Cuis-dev wrote:
> On 9/9/2019 3:17 AM, Philip Bernhart via Cuis-dev wrote:
>> Hello,
>>
>> I noticed that when I register events with when:send:to:with: with 
>> collections
>> as the with: paramater the contents of the collection the collection
>> becomes nil on the actually triggered event. Maybe that has something
>> todo with WeakMessageSend. Anyway an example tells better what I mean:
>>
>>
>> browserMorph
>>     setProperty: #editItemAction
>>     toValue: [:repository :record|
>>         | flashcardMorph card mapping |
>>         flashcardMorph _ FlashcardMorph new.
>>         card _ Flashcard new.
>>         card question: (record at: #question ifAbsent: '').
>>         card answer: (record at: #answer ifAbsent: '').
>>
>>         card when: #changed
>>              send: #saveRecordMappingWith:
>>              to: browser
>>              with: {card. record}.
>>
>>         flashcardMorph model: card.
>>         flashcardMorph showAnswer.
>>         flashcardMorph enableEditing.
>>         flashcardMorph openInWorld ].
>>
>> Then in Flashcard there is a foo method which does:
>> self triggerEvent: #changed.
>>
>> Here the {card. record} becomes for some reason (weak reference?) nil
>> and causes that the event isn't triggered. So #saveRecordMappingWith:
>> isn't called.
>>
>> With simple datatypes like Integers this works, with static arrays this
>> seems to work too.
>>
>> Maybe this style of doing that isn't the right one, maybe I'm using the
>> event system for things which it wasn't supposed to do.
>>
>> Suggestions?
>>
>>
>> Thanks for your time,
>> Philip
>
> Hi Philip,
>
> I believe the event system uses WeakMessageSend in all Smalltalk 
> dialects. Folks, please correct me if I'm wrong.
>
> An easy way to fix the problem is to turn your collection into a 
> literal, using backticks:
>
>         card when: #changed
>              send: #saveRecordMappingWith:
>              to: browser
>              with: `{card. record}`.
>
>
> Cheers,

This 'solution' is of course useless in these case, because at compile 
time you neither have card nor record.

I did some experiments to better understand this issue. You can try 
these in a fresh Cuis, in a Workspace:

"This works ok. You get stuff on the Transcript when you click the button"
| observer block morph model |
observer _ Transcript.
block _ [ :blockArgument |
     morph _ PluggableButtonMorph new
         morphExtent: 100 at 40; color: Color random.
     model _ Switch new.
     model when: #changed
         send: #show:
         to: observer
         with: model.
     model onAction: [ model triggerEvent: #changed ].
     model offAction: [ model triggerEvent: #changed ].
     morph model: model.
     morph action: #switch.
     morph openInWorld.
     ].
block value: Smalltalk.

"This doesn't work. You click on the button and nothing happens"
| observer block morph model |
observer _ Transcript.
block _ [ :blockArgument |
     morph _ PluggableButtonMorph new
         morphExtent: 100 at 40; color: Color random.
     model _ Switch new.
     model when: #changed
         send: #show:
         to: observer
         with: {model. blockArgument}.
     model onAction: [ model triggerEvent: #changed ].
     model offAction: [ model triggerEvent: #changed ].
     morph model: model.
     morph action: #switch.
     morph openInWorld.
     ].
block value: Smalltalk.

So, what is happening here? The implementation of WeakMessageSend and 
the event model works ok in the first case. the argument (model) is not 
garbage collected, because there are strong references to it, and it 
works ok.

On the second case, the arguments (model and blockArgument) are also 
held strongly, and not garbage collected. The problem is with the array 
{model. blockArgument}. This array does get collected, and at the time 
of dispatch it has become nil, and the dispatch is not done. The 
simplest solution is to avoid the array. For that, the message to be 
dispatched needs to have two arguments. Now define #show:show: with an 
obvious implementation, and try:

"This finally works as desired"
| observer block morph model |
observer _ Transcript.
block _ [ :blockArgument |
     morph _ PluggableButtonMorph new
         morphExtent: 100 at 40; color: Color random.
     model _ Switch new.
     model when: #changed
         send: #show:show:
         to: observer
         withArguments: {model. blockArgument}.
     model onAction: [ model triggerEvent: #changed ].
     model offAction: [ model triggerEvent: #changed ].
     morph model: model.
     morph action: #switch.
     morph openInWorld.
     ].
block value: Smalltalk.

Now the WeakMessageSend has two arguments, and it works as expected.

Philip, can you turn your #saveRecordMappingWith: into 
#saveRecordMappingWith:with:, and call #when:send:to:withArguments:?

Cheers,

-- 
Juan Vuletich
www.cuis-smalltalk.org
https://github.com/Cuis-Smalltalk/Cuis-Smalltalk-Dev
https://github.com/jvuletich
https://www.linkedin.com/in/juan-vuletich-75611b3
@JuanVuletich



More information about the Cuis-dev mailing list