Archivi tag: programmazione

core_midi_devel

Come mostrare il tempo di una MusicSequence come nel display LCD di GarageBand

Con un nostro precedente post abbiamo già accennato a questo problema: ora approfondiamo le motivazioni tecnico/teoriche che sono alla base.

Il formato ha 4 valori:

– la misura (bar)
– la battuta (beat)
– la nota di sedicesimo nella battuta
– sottodivisioni della battuta (subbeatdivision)

Il framefork AudioToolbox (Core Audio) offre alcuni strumenti per gestire la divisione del tempo a
supporto di una MusicSequence: i principali sono le seguenti:

  • MusicTimeStamp
  • CABarBeatTime

Il primo è un valore temporale la cui unità è la misura musicale (bar): quindi posto 1 il tempo di durata di una misura, tutto è multiplo o sottomultiplo di questo: ne consegue che MusicTimeStamp è un valore in virgola mobile con la necessità di una certa precisione.

La seconda è una struttura utilizzata per le conversioni (vedi le funzioni MusicSequenceBeatsToBarBeatTime e MusicSequenceBarBeatTimeToBeats), ma la scarsa documentazione non fornisce quanto serve a che queste funzioni facciano a pieno il loro dovere.

Il lato oscuro di queste funzione è il parametro inSubbeatDivisor (per la funzione MusicSequenceBeatsToBarBeatTime) o il campo subbeatDivisor nella struttura CABarBearTime (da passare alla funzione MusicSequenceBarBeatTimeToBeats).

Nella soluzione proposta nel precedente post utilizzavamo la risoluzione della sequenza (ppq) come parametro, e sembrava funzionare egregiamente: invero su divisioni di tempo differenti avremmo potuto avere problemi di rappresentazione.

Infatti il subbeatDivisor è un valore in relazione non solo alla risoluzione (in ppq), ma anche alla divisione del tempo corrente (time signature) e per la precisione al denominatore della divisione del tempo, secondo la sequente relazione:

subbeatDivisor = ( (ppq >> 2) << sig_denominator )

dove sig_denominator è espresso come l’esponente di una potenza di 2 che determina il denominatore della divisione temporale corrente. Una breve nota: il memorizzare l’esponente è un meccanismo utilizzato anche nel meta evento di una SMF o di una MusicTrack: la Tempo Track di una MusicSequence è una MusicTrack, pertanto la divisione è registrata in questa mediante meta eventi (di tipo kMIDIMetaEvent_Time_Signature).

La risoluzione è espressa in parti (tick o pulse) di una nota di quarto (ppq o ppqn), ma quando cambia il denominatore della divisione temporale, allora per avere medesima “precisione” occorre ampliare o ridurre questo valore quando esprimiamo una posizione nel tempo al fine di consentire di avere lo stesso numero di parti nel beat (che ha cambiato dimensione per il diverso denominatore della divizione del tempo).

Dato la divisione di 6/8 e una risoluzinone di 240 ppq:

UInt8    tsig_num = 6;
UInt8    tsig_den = 3;        // 2^3 = 8
UInt16    resolution = 240;    // PPQ

allora avremo:

UInt16  subbeatDivisor = ( (resolution >> 2) << tsig_den );

Le dimensioni delle variabili sono conformi a quanto atteso dalle strutture o funzioni in AudioToolbox.

Il penultimo valore da mostrare è il numero della nota di sedicesimo corrente, e come abbiamo visto l’altra volta è ottenibile mediante la seguente:

subbeat / (subbeatDivisor / 4) + 1

dove subbeat rappresente il “pulse” corrente di una nota di quarto, quindi rapportanto alla divisione corrente (subbeatDivisore) se ne prende la quarta parte: 4 * 4 = 16 !

Per eseguire qualche esperimento ecco come scrivere un beat di tanti eventi quanti i tick indicati dalla risoluzione prescelta.

MusicTrack outTrack;
UInt8    tsig_num = 6;
UInt8    tsig_den = 3;
UInt16    resolution = 240;    // PPQ
UInt16  subbeatDivisor = ( (resolution >> 2) << tsig_den );
CABarBeatTime inBarBeatTime;
MIDINoteMessage aNoteMessage;
MusicTimeStamp       outBeats ;

MusicSequenceNewTrack (mainSequence, &outTrack);

aNoteMessage.channel = 1;
aNoteMessage.note = 23 & 0x0f;
aNoteMessage.velocity = 80 & 0x0f;
aNoteMessage.releaseVelocity = 0;
aNoteMessage.duration = 1/960;

for(int i = 0; i < resolution; i++){
inBarBeatTime.bar = 1;
inBarBeatTime.beat = 1;
inBarBeatTime.subbeat = i;
inBarBeatTime.subbeatDivisor = subbeatDivisor;

MusicSequenceBarBeatTimeToBeats(mainSequence, &inBarBeatTime, &outBeats);
MusicTrackNewMIDINoteEvent (outTrack, outBeats, &aNoteMessage);
}

Ovviamente non abbiamo detto della cosa più importante: come leggere il denominatore della divisione in relazione al tempo corrente di riproduzione (o visualizzazione).

Questo sarà oggetto di un altro post che introdurrà la descrizione di un mio progetto di ampliamento del framework AudioToolkit che presto pubblicherò su github.

Per ora basta.

videogame80

Strike and ball NG

Come promesso iniziamo con la prima proposta per la relizzazione del gioco che vi avevamo descritto nell’articolo Amarcord e nuove sfide.

Per sfida e ricerca Nerdammer vi offre sempre soluzioni non banali, poco frequentate o innovative.
Questa volta, pur con lo stesso spirito, seguiamo una soluzione “mainstream”.

Volendo sviluppare il nostro gioco con tecnologie per il web abbiamo fatto ricadere la nostra scelta su una tecnologia che ci offrisse qualche spunto di interesse e ci portasse un minimo di agio nel modellare il progetto con quegli strumenti (tra paradigmi e astrazioni) ai quale siamo abituati nella nostra esperienza di sviluppo software.

Abbiamo scelto il framework (Google) AngularJS.

La realizzazione del nostro gioco (nella versione che proponiamo) non é compito arduo in generale, pertanto un simile progetto non potrà investire tutte le potenzialità di AngularJS, e ce ne dogliamo con voi, ma la semplicità ci consente di descrivere gioco e tecnologie in un unico momento.

L’algoritmo di valutazione della risposta utente impone di verificare dapprima le cifre giuste nella corretta posizione escludendole dalla successiva valutazione delle cifre giusta nella posizione errata. Per procedere a tali valutazioni occorre modificare la risposta utente eliminando via via quanto rilevato. Nel voler far questo sulla risposta utente cosi come ci arriva dalla view ci troviamo ad affrontare il limite del tipo String in EMCA script, che è Immutable.
Per rendere il tipo String Mutable provvediamo alla modifica del suo prototype introducendo un metodo replaceAt() che pur generando una copia modifcata dell’oggetto originale riesce a mimare il comportamento mutable.

Armati di questo nuove strumento implementiamo nel controller il metodo di verifica che sarà innescato da evento click (ng-click) su un pulsante della vista.

Le valutazioni (una stringa di X e O) verranno accodate in un array del controller collegato (one-way-binding) ad un elemento <Ii > della vista: questo verrà gestito dalla direttiva ng-repeat che iterando all’array a cui è collegato provvederà dinamicamente all’aggiornamento di una vista delle risposte date dall’utente.

Questo è essenzialmente il cuore del programma.

Il resto é solo per gestire gli stati di vittoria, sconfitta e resa, le relative statistiche e qualche opzione, il tutto reso attraverso le direttive proprie di AngulaJS, qualche stile e poco altro.

Il sorgente completo lo trovate in https://github.com/andrea-tassotti/sbjs .

videogame80

Amarcord e nuove sfide

Correvano gli anni ’80 (secolo XX!) e nelle case c’erano i cosí detti “home computers”.
Imperava la sfida tra possessori di ZX Spectrum e Commodore 64.
Era l’epoca in cui ci si approcciava alla programmazione con il linguaggio BASIC; i piú arditi si spingevano verso l’assembler (io tra quelli) a colpi di PEEK e POKE (mitiche istruzioni BASIC per accedere direttamente alla memoria di quelle semplici architetture, perciò semplici da comprendere).

Era il tempo in cui eravamo capaci di attendere decine di minuti per caricare da nastro magnetico (una semplice cassetta audio analogica!) in memoria i pochi kbytes (il 64 ne aveva appunto 64, di cui solo 38 per programmi BASIC) dell’agognato video gioco: il tutto da fruire sul TV di casa.
I piú fortunati poterono godere dell’accesso diretto (e della relativa velocitá) dei floppy disk da 5″1/2 per la bellezza di 176k (unità 1541), o di un monitor a colori (1701/1702)!

Molti giochi erano proprio in BASIC, trascritti da pagine di riviste di computer (cosa sono?), frutto di scambi e baratto. Spesso si ignoravano origine e autore.

Vedere un super calcolatore giocare a Tic TacToc (Tris) nel film “War Games” nello stesso modo (e sostanzialmente qualità grafica ) del nostro home computer era parte di una forma di orgoglio di essere nel futuro, nella modernità, al passo con la tecnologia.

The-Only-Winning-Move-is-NOT-to-Play-320x177

Se aggiungiamo il fatto che il gioco lo avevamo “scritto di nostro pugno”, é facile immaginare quali emozioni potevano correre nei nostri cuori.

Di quei giochi semplici si é perso un po’ ricordo, e non sono stati piú riproposti (con qualche eccezione come componente ludica nei primi cellulari Nokia: ricordate Snakes?).

Snakec64
E quindi lanciamo qui una sfida.

Cominciamo una “operazione amarcord”, ossia cominciamo a recuperare al ricordo e alla fruibilità questi vecchi e gloriosi giochi.
Noi inizieremo da uno: cerchiamo volenterosi che proseguano la sfida.

La mia scelta cade sul ricordo di un gioco il cui nome era noto a me come Strike & Ball, gioco che mi ha appassionato sia nella sua implementazione (ne avevo esteso le caratteristiche di base e realizzato una interfaccia molto accattivante per i mezzi disponibili), sia nel suo utilizzo.

Grazie ad Internet (e Wikipedia), inesistente negli anni ’80, scopro che questo glorioso gioco aveva un padre illustre (e non si tratta dell’ovvio Mastermind che deve suoi natali allo stesso padre).
Si tratta di Bulls and Cows un gioco semplice che può essere giocato tra amici anche semplicemente con carta e penna.
La cosa per me piú affascinante é che il gioco é da ritenersi all’origine del primo “videogioco” della storia: nel 1970 infatti J.M.Grochow al MIT scrisse in PL/1 su Multics (cavolo! il padre di Unix! … quasi una riunione di famiglia!) un programma dal nome moo (esplicativo, no?!) per giocare a Bulls and Cows.

Le regole di gioco (nella versione numerica, ossia quella da cui é derivata la versione del 1970) sono molto semplici.
Si tratta di indovinare in un numero finito di tentativi un numero segreto di 4 cifre; ad ogni tentativo l’avversario (nel nostro caso il computer) indicherà quante cifre sono state individuate nella posizione corretta con il simbolo X (Strike)  e quante invece sono state indicate giuste ma nella posizione errata con il simbolo O (Ball).

Si vince indovinando in numero segreto; si perde esaurendo i tentativi concessi (tipicamente 10).

Sembra facile, ma vi assicuro che consente di impegnare un po’ di “materia grigia”!

Nella versione originale un progamma al calcolatore impiega in media circa 5 tentativi per indovinare il numero segreto! Voi?

Ovviamente si può “alzare l’asticella” elevando il numero di cifre per il numero segreto.
Alla prossima puntata con due proposte di implementazione per questo gioco.

P.S. Forse ne troverete in giro, anche Open Source: ma noi cercheremo di stupirvi!

cocoa_cup

Introduzione a Cocoa – Parte III

KVO, KVC & Binding
Key-Value Observing (KVO) è un meccanismo che consente agli oggetti di ricevere notifiche su cambiamenti di specifiche proprietà di altri oggetti.

E’ basato sul protocollo informale NSKeyValueObserving.

Le proprietà osservate possono essere semplici attributi, relazioni uno-a-uno o uno-a-molti. Nel contesto MVC, questo meccanismo è particolarmente importante in quanto consente agli oggetti della vista di osservare i cambiamenti negli oggetti del modello attraverso il livello controllo.
Essenziale in questo la tecnologia Cocoa Bindings.

Cocoa fornisce molti oggetti della capacità di essere osservati attraverso il protocollo NSKeyValueObserving.

Key-Value Coding
Key-Value Coding (KVC) è un meccanismo per accedere in modo indiretto agli attributi e relazioni degli oggetti usando identificatori stringa.
kvc

Tecnica sottointesa da molti meccanismi e tecnologie Cocoa; particolare rilevanza ha invero nella programmazione di applicazioni Core Data, con uso intensivo di binding, e altro.
Il meccanismo KVC è reso possibile da protocollo informale NSKeyValueCoding.
Due metodi di questo protocollo (valueForKey: e setValue:forKey:) sono particolarmente importanti in quanto consentono di leggere e scrivere valori delle proprietà di oggetti conformi mediante una chiave di accesso nominale (una stringa con il nome della proprietà). NSOject fornisce una implementazione base di questi metodi. Specializzazioni sono possibili.
Implementare questi metodi può significare dotare le classi di proprietà “virtuali”, ossia non corrispondenti a effettivi attributi negli oggetti, ma “calcolati” o comunque gestiti in modo meno diretto (composti da più proprietà, ecc).
Stabilire collegamenti tra oggetti via KVO è una capacità di Interface Builder.

Cocoa e metafore di interfaccia grafica
Cocoa trova soluzioni per le seguenti metafore:

  • gestione finestre
  • menu statici vs dinamici
  • undo e redo
  • drag & drop (vedi protocollo NSTableViewDataSource)

Cocoa, Applicazioni e Interface Builder
Nell’architettura complessiva di un’applicazione Cocoa entra in gioco anche il concetto di “bundle”.
L’applicazione MacOSX è un insieme (bundle, per l’appunto) di risorse: non solo il codice eseguibile, ma anche descrizioni formali dell’interfaccia, immagini, dati, ecc.
La costruzione di questo insieme è demandata a XCode; nello specifico vendono posti insieme il prodotto della compilazione ed in particolare tutto quanto è compreso nella cartella Resource e del progetto corrente.

In particolare in questa cartella è presente un NIB (ad oggi in formato binario, dunque si parla di XIB, ma per quel che segue è del tutto equivalente) un archivio di informazioni prodotto dal programma Interface Builder che, come appare ovvio, riguarderanno la descrizione formale delle componenti di interfaccia grafica.
xib

In realtà la cosa è più generica: nella maggiorparte dei casi Interface Builder gestisce informazioni per l’interfaccia grafica, ma nei casi pratici può qualcosa di più.
Quando si parla di informazioni per l’interfaccia, in realtà si deve parlare propriamente di istanze di oggetti di classi Cocoa (in particolare di ApplicationKit) capaci di astrarre concetti come finestra, menu, casella di testo, ecc.
Interface Builder dunque istanzia (crea) oggetti di interfaccia: la descrizione dell’interfaccia dunque altro non è che un “contenitore di istanze di oggetti”, un concetto che apparentemente sembra una cosa strana ma che è semplicemente spiegabile introducendo il pattern di programmazione della serializzazione di oggetti.
Interface Builder istanzia dunque oggetti Cocoa (ogni icona nella sua finestra che elenca il contenuto di un NIB è una istanza) mentre l’utente programatore “disegna” l’interfaccia; in seguito li “serializza” nel file NIB/XIB.
Il processo di deserializzazione avviene alla partenza della applicazione che si ritroverà dunque già create tutte le istanze degli oggetti introdotti da Interface Builder.
A ciascun oggetto creato dal NIB verrà inviato il messaggio (leggi “verrà eseguito il metodo”) initWithCoder: e awakeFromNib:.
Eventuali specializzazioni di oggetti (classi utente derivare da qualche classe del framework) potranno governare queste fasi implementando una loro versione di questi metodi.
Ogni icona in Interface Builder rappresenta una istanza di una certa classe.

La seguente icona
object
rappresenta ad esempio una istanza di una classe NSObject o di una versione personalizzata (in questo caso il tipo è da indicare esplicitamente nella finestra inspector di Interface Builder).

Questa è una possibilità di generare istanze di oggetti, anche utente, in maniera visuale e con nascita legata all’avvio dell’applicazione, e dunque esistenti “per costruzione”.

Allocazione di oggetti nel codice ObjC
Diversa la sorte di una istanza di oggetto creata da codice utente.
Tipicamente l’allocazione avviene per mezzo del messaggio statico alloc: inviato ad una classe, a cui segue il messaggio init: (semplice o in qualche variante con argomenti) inviato all’istanza ottenuta.

[[NSObject alloc] init];

Qualora le specializzazioni utente delle classi abbiano la necessità di istanziare oggetti a partire da un NIB e a partire da codice devono implementare entrambi i messaggi initWithCoder: che init: ed eventuali varianti.

Oggetti e messaggi
Gli oggetti Cocoa sono progettati per essere immersi in un circuito di invio reciproco di messaggi atti a costituire quella collaborazione tra oggetti che in object-oriented produce l’essenza di un programma.
E’ dunque importante conoscere, specie nell’ottica della specializzazione, significato e ordine di invio di ciascun messaggio.
Raramente sarà il nostro codice specializzato a inviare messaggi; più frequentemente dovremo analizzare e realizzare le specializzazioni in relazione alla catena di messaggi che “l’applicazione” (vista in una ottica di autonomia di funzionamento) invia alle sue componenti, anche in relazione ad interazione con l’utente (eventi).

Continua

cocoa_cup

Introduzione a Cocoa – Parte II

Cocoa e pattern di programmazione
Cocoa è un framework complesso. Nella sua progettazione ed implementazione sono utilizzate molteplici tecniche di progettazione ad oggetti.
Tra queste ricordiamo l’adozione dei seguenti pattern di programmazione:

  • Modello-Vista-Controller (MVC)
  • Delega
  • Delega di data source
  • Observer – KVC, KVO & binding
  • Notifica

L’adozione di tali modelli di programmazione è praticamente obbligatoria: nella adozione più semplice (senza coinvolgere il progetto di nuove classi) si esplicita nell’individuare specifici oggetti (anche realizzati da utente) capaci di essere attori per uno di questi ruoli.
Cerchiamo di specificare brevemente.

Delega
Il processo di delega è un modo che una istanza di oggetto ha per “traslare” l’implementazione di un suo comportamento (metodo) con il metodo omonimo appartenente ad un altro oggetto (anche di tipo diverso).
Il processo di delega è una forma di composizione (composizione dei comportamenti) tra oggetti.
delega

Una classe che prevede processo di delega per i suoi oggetti avrà un attributo indicante l’oggetto delegato (delegate). Per classi gestite con Interface Builder è sufficiente collegare l’attributo delegato di un oggetto ad un altro oggetto per stabilire la possibilità che il processo di delega abbia luogo.
Nel processo di delega, uno stesso oggetto delegato può essere delegato ad implementare più di un compito (metodo).
Vediamo come esempio quali metodi vengono delegati dalla classe NSTableView ad un suo oggetto delegato:

  • tableView:willDisplayCell:forTableColumn:row:
  • tableView:shouldSelectRow:
  • tableView:shouldSelectTableColumn:
  • selectionShouldChangeInTableView:
  • tableView:shouldEditTableColumn:row:

Modello-Vista-Controllo (MVC – Model/View/Controller)
Pattern di programmazione storico introdotto da SmallTalk e comune a tutti gli approcci ad oggetti alle interfacce grafiche (Java compreso).
Si fonda sulla separazione logica ed implementativa della rappresentazione dei dati dell’applicazione (modello), della componente visuale di interfaccia utente (vista) e della componente che coordina i due e costituisca tutto quanto è eventualmente procedurale nella applicazione (controller).
Una applicazione Cocoa è fortemente conforme al pattern MVC.
Non a caso il framework introduce classi e derivazioni da queste per rappresentare una implementazione delle componenti conforme al modello, con forme e comportamenti prestabiliti a governare le più disparate esigenze.
Per Vista e Controllo abbiamo:

  • NSView
  • NSController

Per il modello si possono usare tutte le classi conforme al modello “container” o “collection” quali:

  • NSArray
  • NSSet

oppure la classe NSManagedObject con l’adozione del framework Core Data (>= 10.4).
mvc

Composizione
La composizione è una tecnica di ADT (Abstract Type Definition) per la composizione delle informazioni degli oggetti. In progettazione object-oriented viene indicata come avente maggiori vantaggi dell’ereditarietà (si prefersce in sostanza descrivere i problemi con relazione ha-un invece che è-un, per ridurre la complessità della gerarchia di derivazione o eventuali effetti collaterali).
Questa filosofia è adottata anche da Cocoa.
Alcune istanze di classe dell’ApplicationKit esistono solo come elementi parte di classi più grandi come “aiuto all’implementazione”.
Un esempio classico è la NSTextFieldCell, che incapsulata in unica istanza in una NSTableView costituisce l’elemento che “disegna” tutte le celle della tabella, riutilizzata più volte dall’algoritmo di disegno della classe NSTableView.
In questo caso si ha anche una scelta architetturale particolare per cui non si incapsulano n istanze di celle per quante sono gli elementi della tabella (aumento di complessità), ma una sola istanza che non è un vero oggetto grafico (non deriva da NSView), ma è uno strumento che implementa il comportamento grafico del “disegnatore”.

Protocolli
Un protocollo è un modo alternativo alla derivazione per rendere possibile l’intoperabilità di classi molto differenti che devono rispondere ai medesimi messaggi.
L’insieme dei messaggi in comune, invece di essere una interfaccia, diviene un protocollo.
Un protocollo diviene così una interfaccia che qualsiasi classe può adottare in maniera indipendente dal progetto.
Esistono due tipi di protocolli in objc: formali ed informali.
Un protocollo informale è sostanzialmente una categoria di NSObject. Contrariamente ad una interfaccia, un protocollo informale non è da implementarsi obbligatoriamente. Prima che sia invocato un metodo, l’oggetto chiamante controlla se l’oggetto destinatario del messaggio implementa il metodo. Dato che i metodi opzionali di un protocollo sono stati introdotti solo nel Objective-C 2.0, i protocolli informali sono stati essenziali nel modo in cui le classi di Foundation e AppKit hanno implementato la delega..

NSObject protocols:

  • autorelease
  • class
  • conformsToProtocol:
  • description
  • hash
  • isEqual:
  • isKindOfClass:
  • isMemberOfClass:
  • isProxy
  • performSelector:
  • performSelector:withObject:
  • performSelector:withObject:withObject:
  • release
  • respondsToSelector:
  • retain
  • retainCount
  • self
  • superclass
  • zone

Un protocollo formale dichiara una lista di metodi che ci si aspetta utilizzata da una classe utente. Hanno una propria sintassi per dichiarazione, adozione e controllo di tipo.

protocollo_formale

Esempio particolare di protocollo: NSTableViewDataSource
Il metodo setDataSource: di una NSTableView vuole un oggetto conforme ad protocollo informale NSTableVewDataSource:

- (void)setDataSource:(id<NSTableViewDataSource>)anObject

L’oggetto che viene impostato come “sorgente dati” sarà in realtà un oggetto sottoposto a processo di delega che risponde al protocollo informale richiesto.

In particolare delega i seguenti compiti (che sono dettati dal protocollo):

Delegate Message Significato
numberOfRowsInTableView: Lettura valori
tableView:objectValueForTableColumn:row: Lettura valori
tableView:setObjectValue:forTableColumn:row: Impostazione valori
tableView:acceptDrop:row:dropOperation: Dragging
tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes: Dragging
tableView:validateDrop:proposedRow:proposedDropOperation: Dragging
tableView:writeRowsWithIndexes:toPasteboard: Dragging
tableView:sortDescriptorsDidChange: Ordinamento

Osservatori e Notifiche
L’Observer pattern è un design pattern utilizzato per tenere sotto controllo lo stato di diversi oggetti.

Observer
Un observer definisce una dipendenza uno-a-molti tra oggetti al fine di notificare e aggiornare automaticamente tutti gli oggetti dipendenti al cambiamento dello stato dell’oggetto osservato. Il pattern Observer è essenzialmente un modello “pubblica e sottoscrivi” in cui un soggetto e i suoi osservatori sono non strettamente accoppiati. La comunicazione può avvenire tra gli osservatori e gli osservati senza che l’uno sappia molto dell’altro.

Notifications
Il meccanismo di notifica in Cocoa implementa un sistema diffusione messaggi uno-a-molti in accoro con il Observer pattern. Gli oggetti di un programma aggiungono se stessi o altri oggetti ad una lista di osservatori per una o più notifiche, ciascuna delle quali viene identificata da una stringa (nome della notifica).
notification
Gli oggetti delegati vengono automaticamente registrati per ricevere messaggi corrispondenti alle notifiche.

Esempio: delegato di una NSTableView. Questi messaggi informano il delegato quando la selezione è stata modificata o quando una colonna viene mossa o ridimensinoata:

Delegate Message Notification
tableViewColumnDidMove: NSTableViewColumnDidMoveNotification
tableViewColumnDidResize: NSTableViewColumnDidResizeNotification
tableViewSelectionDidChange: NSTableViewSelectionDidChangeNotification
tableViewSelectionIsChanging: NSTableViewSelectionIsChangingNotification

 

Continua

cocoa_cup

Introduzione a Cocoa – Parte I

Con questo post vogliamo iniziare una serie di articoli più astratti sulla programmazione Cocoa, quasi un tutorial.

La programmazione in ambito nativo MacOSX si fonda su due pilastri: Objective-C e Cocoa Framework. Questo documento non si prefigge lo scopo di coprire lo studio particolareggiato di queste tecnologie (non potrebbe), ma di fornire un rapido accesso allo sviluppo software MacOSX.
Per poter governare la programmazione in ambito MacOSX occorre conoscere inoltre gli strumenti XCode, ivi compresa la componente Interface Builder per lo sviluppo delle interfacce grafiche (prima di Xcode 4 era una applicazione esterna).
La programmazione Objective-C è una programmazione multi-paradigma: l’Objective-C (da adesso objc) si fonda sul linguaggio C (programmazione procedurale) e costruisce su questo una sovrastruttura per il paradigma Object-Oriented (con l’introduzione di specifiche sintassi).
Per l’implementazione del paradigma ad oggetti, l’objc introduce un runtime environment che costituisce un “motore” (utilizzato da ogni applicazione costruita in objective-c) atto a realizzare la metafora dei messaggi e la componente dinamica del modello ad oggetti del linguaggio (polimorfismo, dynamic binding, ecc).

L’adozione del modello ad oggetti viene supportata da alcuni framework: primo tra tutti Foundation (Origina da NextStep Foundation library), responsabile della costruzione organica del modello a partire dalla classe NSObject.

  • Tutte le classi di questo framework hanno nomi con prefisso “NS”, che intende ricordare l’origine in NextStep.

Nel contesto grafico MacOSX il principale framework adottato è Cocoa: questo estende Foundation con l’adozione di tutto un insieme di framework tematici. Anche in questo caso le classi hanno nomi con prefisso “NS”.
Il corollario di framework che costituiscono Cocoa si posizionano su diversi livelli di una gerarchia di astrazione delle componenti del S.O. e altro: ogni framework è dato per semplificare e riutilizzare un progetto relativo a situazioni specifiche (Core Audio, Core Graphics, Core Animation, Core Data, ecc).

  • Le classi appartenenti a questi framework possono essere riconosciute sempre attraverso la convenzione dei prefissi, ma non in senso rigoroso (es. quelle Core Data hanno esempio prefisso “NS”, mentre Core Graphics ha “CG”).
  • La forma dei prefissi è legata a fattori storici e gerarchici.

Tutti questi framework sono da riferirsi ad uno SDK relativo alla versione software del sistema operativo: un SDK ha un numero di versione pari al numero di versione del S.O. (es. SDK per Tiger è 10.4, per Leopard è 10.5, per Snow Leopard 10.6, per Lion 10.7, Mountain Lion 10.8, Mavericks 10.9).

API Compliance
Un’applicazione può essere compilata solo per uno specifico SDK (selezione che avviene nella configurazione del “target” in XCode); si può adottare una programmazione parametrica (ovvero con direttive condizionali di preprocessing) al fine di modellare il codice per applicare modifiche atte a compilare per specifico SDK e modulare le eventuali differenze rispetto ad un altro.

#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
// codice per Leopard e oltre
#else
// codice pre-Leopard
#endif

ABI Compliance
Un’applicazione può essere invero compilata per architetture differenti (PPC vs Intel): questo è l’adozione dei concetti di Universal Binary.

  • In questo caso Lion ha creato uno spartiacque: la soppressione di Rosetta e l’impossibilità di sviluppare codice per PPC ha impedito lo sviluppo in un unico progetto per più piattaforme.

Cocoa e GNUStep
GNUStep è una versione “open” di NextStep, e per questo ha molte similitudini con Cocoa.
Un codice realizzato in Cocoa è “riutilizzabile” in contesto GNUStep. Gli strumenti di sviluppo di GNUStep sono però gli originali Project Builder e Interface Builder di NextStep e divergono dunque per alcuni aspetti dalle novità introdotte da MacOSX e da XCode.
Riutilizzare il codice è possibile ma occorre riportarlo all’interno di un progetto GNUStep.
Il vincolo maggiore è il livello di compatibilità rispetto ai vari SDK disponibili nel mondo MacOSX: attualmente GNUStep è paragonabile a Panther, ossia 10.3. Un software calibrato per 10.4 oppure adottante framework particolari come Core Data, Core Audio o Core Animation, difficilmente saranno “portabili” nel mondo GNUStep.
Lo saranno solo se soluzioni a quelle differenze vengano prodotte dal progetto GNUStep stesso oppure in modo autonomo da chi sviluppa il software.

Cocoa runtime environment
Possiamo parlare di un vero e proprio runtime environment anche nel caso del framework Cocoa.
Il concetto di “applicazione” è proprio del framework, il quale offre un suo modello e una implementazione. In particolare il progetto riguardante il concetto di applicazione è parte dello specifico framework ApplicationKit.
Possiamo così leggittimamente parlare di “applicazioni Cocoa” contro “applicazioni BSD“, benchè in entrambi i casi si possa adottare objc e Foundation.
Cocoa suddivide il concetto di “applicazione” in due possibili modelli alternativi:

  • applicazione
  • applicazione basate su documento

Applicazione
Una applicazione incentrata su un ciclo di eventi che pongono in relazione gli elementi grafici presenti (finestre, menu, ecc).

Applicazione basata su document
Una applicazione il cui fulcro è un “gestore di documenti”; ciascun documento ha una propria finestra di interfaccia e una propria base dati di riferimento. Ciò che viene implementato in riferimento ad un documento astratto viene replicato (nei comportamenti) per ciascun docuento “aperto”.

Entry point di un programma Cocoa
Proprio in ragione della presa in carico da parte del framework del progetto “applicazione”, non esiste un “corpo principale del programma” come in altri ambienti di sviluppi.
Per tutte le applicazioni Cocoa si ha un unico “punti di inizio del programm” che è l’esecuzione della funzione NSApplicationMain(). Questo viene precostruito dal progetto modello in XCode.

int main(int argc, char *argv[])
{
return NSApplicationMain(argc,  (const char **) argv);
}
  • Ovviamente è data la possibilità di interferire con le caratteristiche base dell’applicazione (intesa come flusso principale di programma) attraverso alcune classi di ApplicationKit (utilizzando metodi e attributi di classe specifici) e implementando una versione ad-hoc della funzione NSApplicationMain().

Classi che riguardano l’applicazione sono: NSApplication, NSBundle, NSApp.
La complessità dell’intera architettura è tale che non può essere ripercorsa da questo documento introduttivo.

Continua.