_NSBindingAdaptor: Under ytan i binderträsket, del 1

[editerad 081023, förtydligat exemplet som visar att dataflöde baklänges inte fungerar]
Jag har inte kunnat lämna den odokumenterade NSBinderklassen och klassklustret runt om denna, utan sökt vidare. I denna artikel spanar vi speciellt på _NSBindingAdaptor. I analysen används fantastiska Nu . Lisp & Cocoa i ett…

Ola Bäckström 9 juli 2008

F-script är ju väldigt polerat och lättillgängligt – bara att hämta binären och köra. Nu däremot kräver att man hämtar hem och bygger själv. Tröskeln känns högre… men när man väl lyckats med det och startar nush (den fristående Nu-tolken) skingras mörkret. En välbryggd Cocoa-Lispinterpreter står beredd att hjälpa oss i ned under ytan, bortanför Apple-dokumenterat område.

Ånej ännu ett språk?

Har man den minsta lispbakgrund och god kännedom om Obj-C klarar man snabbt av den nya syntaxen. Här är ett exempel:

(class TTWrap is NSObject
	(ivar (id) val)
	(ivar-accessors))

(class TTCrap is NSObject
	(ivar (id) attr)
	(ivar-accessors))

Koden ovan skapar två nya klasser TTWrap och TTCrap med varsin attribut (instansvariabel), kallad val och attr. Eftersom vi ber snällt att få accessorer fixade är vi klara. Enklare än Obj-C!

Sedan behöver vi två instanser, en av varje klass:

(set x (TTWrap new))
(set y (TTCrap new))

Varje rad kan jämföras med x=[TTWrap new]; i Obj-C, där x är en global variabel av typen id.

Sedan sätter vi attributen via de automatgenererade setterna:

(x setVal:"apa")
(y setAttr:"traktor")

detta är liknar ju vanlig Obj-C något oerhört. För att dubbelkolla att vi gör rätt printar vi ut värdet av y:

% y
<TTCrap:3435f0>
%

(procenttecknet är nushprompten)
Öhh? jaja… vi fick ut att y ligger på addressen 0x3435f0 och är av klassen TTCrap. Bra, men värdet på attributen?

% (y attr)
apa
%

Finfint! Nu kan vi gå vidare.

Hur kommer _NSBinderAdapter in i bildern?

Nu när vi har två objekt kan vi binda ihop dem:

(y bind:"attr" toObject:x withKeyPath:"val" options:nil) 

Vanlig binding alltså. Vi knyter ihop x.val med y.attr. Vi kollar att bindningen fungerar

% (y attr)
apa
% (x setVal:"gorilla")
% (y attr)
gorilla
% 

y.attr har plötsligt fått samma värde som x.val, och vidare uppdateras y.attr av nya värden på x.val. Precis som det ska.

Dataflöde baklänges fungerar inte:

% (y setAttr:"nytt")
% (y attr)
nytt
% (x val)
"gorilla"
% 

Denna enkelrättade bindning gäller för oss när vi bundit två attribut med AppKit standard-bindningar.

Man nu börjar äventyret. Slå

% (y _bindingAdaptor)
<_NSBindingAdaptor:34c590>
%

Va? har y en bindnings-vadå? Har x det också?

% (x _bindingAdaptor)
()
% 

Nej (en tom lista är lisps/Nus sätt att säga nil).

Vad som skett är att y fått en bindningsadapter. En spännande grej, vi ska spana mer på den, men först ska vi bara kolla upp KVO-kunnskapen. (Du minns väl att KVB bygger ovanpå KVO?)

Finns KVO-proxyobjekt på riktigt?

Ett proxyobjekt borde ha poppat in och ersatt det observerade objektet, x alltså:

% x
<NSKVONotifying_TTWrap:34c870>
% y
<TTCrap:3435f0>
% 

Riktigt! Via isa-swappning har x plötsligt blivit av klassen NSKVONotifying_TTWrap. Spännande, och intressant är att ber man detta objekt om vilken klass den är av:

% (x class)
TTWrap
% 

Illusionen är total: proxyklassen skickar vidare nästan allt till den vanliga TTWrap klassen.

,

---
---