• Programmazione Android
  • CORSI ONLINE
  • Web Agency

Logo

Corsi di programmazione web e mobile online
Navigation
  • Home
  • CORSI ONLINE
  • Tutorial Pratici
  • GUIDE COMPLETE
    • Corso completo di C
    • Corso videogame con Cocos2d
    • Programmazione Cocoa Touch
  • Sezioni
    • Libri e manuali
    • Tips & Tricks
    • Risorse utili
    • Strumenti di Sviluppo
    • Materiale OpenSource
    • Framework
    • Guide Teoriche
    • Guide varie
    • Grafica e Design
    • iPad
    • News
    • Video Tutorial
    • Windows Phone
  • Pubblicità
  • About
    • Chi siamo
    • Pubblicazioni
    • Collabora
    • Sostieni devAPP

Uno sguardo al runtime Objective-c

By IgnazioC | on 10 Settembre 2013 | 0 Comment
Guide Teoriche

Quando si parla di linguaggio di programmazione Objective-C, abbreviato spesso con Obj-c, una tra le prime informazioni che viene data è che l’intero linguaggio è un superset del linguaggio standard C. Ciò significa in breve che all’interno di un programma obj-c potete scrivere tutto il codice C che vi pare, questo funzionerà perfettamente. Questo permette, tra l’altro, di poter riutilizzare moltissime librerie “classiche” scritte in C all’interno dei nostri programmi, come le socket BSD e molto altro.

Obj-C ha un’altra particolarità degna di nota, che va sotto il nome di Binding Dinamico (wikipedia). Il Binding è il processo che viene eseguito dal compilatore o dall’interprete che associa una particolare parte di codice al nome di una funzione. È, in altri termini, quel processo che ci permette di invocare una parte di codice utilizzando il nome della funzione associata.

In Obj-C questo processo viene eseguito a runtime, viene cioè fatto durante l’esecuzione del programma stesso e prende il nome di Binding Dinamico, mentre negli altri linguaggi viene effettuato a compile time, ovvero durante la fase di compilazione. In quest’ultimo caso si chiama Binding statico.

Il binding in C

Il binding è un argomento piuttosto tecnico ma per capire il concetto alla base vediamo due esempi in C, in questo primo programma viene semplicemente richiamata la funzione doppio() quindi niente di particolare. (eseguite il codice qui: http://ideone.com/l4KIYx)

#include 

int doppio(int n) {
	return n * 2;
}

int main(void) {
	int input = 42;
	int risultato = doppio(input);

	printf("Il risultato: %d", risultato);
	return 0;
}

In questo caso il compilatore “semplicemente” sostituisce alla chiamata alla funzione doppio() il suo indirizzo di memoria.

In questo secondo esempio abbiamo invece un puntatore a funzione, che viene assegnato alla funzione doppio() o triplo() a seconda se il valore è pari o dispari. (esegui il codice qui: http://ideone.com/IkYy9B)

#include 

int doppio(int n) {
	return n * 2;
}
int triplo (int n) {
	return n * 3;
}

int main(void) {
	int k = 3;
	int risultato;

	int (*func)(int k);

	if (k % 2 == 0) {
		func = doppio;
	} else {
		func = triplo;
	}

	risultato = func(k);
	printf("Il risultato: %d", risultato);

	return 0;
}

Quello che  si vede in questo esempio è che usando i puntatori a funzione possiamo, a runtime, switchare il comportamento della funzione tra una implementazione o un’altra. Possiamo, cioè, con lo stesso nome invocare parti di codice completamente distinte.

Questo ragionamento un po’ articolato è indispensabile se si vuole scrivere codice ad oggetti con un linguaggio che nativamente non è ad oggetti ed è infatti alla base del funzionamento dell’Obj-c.

In Obj-c tutta la parte che si occupa del binding è inserita in una libreria che si chiama “Objective-C runtime” e della quale potete leggere qui (http://goo.gl/Tc2y3j) la documentazione e qui (http://goo.gl/fd1U6A) la programming guide.

Questa libreria è molto importante anche per altri argomenti che vanno oltre lo scopo di questo articolo, come l’introspection e la metaprogrammazione.

Objc_msgsend

Cosa succede dietro le quinte del linguaggio Obj-C quando viene inviato un messaggio? Cosa fa di preciso una semplice riga come questa?

id returnvalue = [anobject messageName:parameter];

Il compilatore traduce questa riga in puro C e traducendo l’invio del messaggio in una chiamata alla funzione objc_msgSend() il cui prototipo è questo:

id objc_msgSend(id theReceiver, SEL theSelector, ...)

Questa “traduzione” produce questa riga di codice

id returnvalue = objc_msgsend(anobject, @selector(messageName:), parameter);

Per trovare la corretta implementazione del selector messageName l’obj-c runtime analizza la mappa dei metodi dell’oggetto anobject. Questa mappa contiene da una parte il selector (che è una struct C) e dall’altra un puntatore a funzione.
Se il runtime non trova il selector all’interno di questa mappa allora segue il puntatore “isa” dell’oggetto per risalire nella gerarchia delle classi.
Se per esempio invochiamo il metodo alloc sulla classe NSString, nella sua mappa non ci sarà alcuna entry per questo selector e in runtime proseguirà nella ricerca sulla classe NSObject che è l’immediata superclass di NSString.

Swizzling

La tecnica dello swizzling constite nell’invertire a runtime l’implementazione di due metodi ed è quindi possibile soltanto negli ambienti con il binding dinamico.
Obj-c si presta bene a questo tipo di tecnica che, anche se è un po’ “oscura”, si può rivelare molto utile in certi casi.

Create un progetto xcode di qualsiasi tipo, in questo caso io ho utilizzato “console application” perché non ho bisogno di alcuna interfaccia per questo esercizio.

All’interno del progetto create una classe con due metodi:

//file Dog.h
#import 

@interface Dog : NSObject
-(NSString *)bark;
-(NSString *)growl;
@end

//file Dog.m
#import "Dog.h"

@implementation Dog
-(NSString *)bark {
    return @"WOOF";
}

-(NSString *)growl {
    return @"GRRRR";
}
@end

Se avete creato un progetto di tipo console andate a modificare il file main, viceversa utilizzate il vostro delegate.
La prima cosa da fare è verificare il comportamento della nostra classe, quindi scriviamo un piccolo esempio all’interno del main

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Dog *dog1 = [[Dog alloc] init];
        NSLog(@"Bark: %@", [dog1 bark]);
        NSLog(@"Growl: %@", [dog1 growl]);
}
}

l’output sarà senza grosse sorprese:

2013-09-07 09:52:40.646 learnSwizzling[95797:303] Bark: WOOF
2013-09-07 09:52:40.649 learnSwizzling[95797:303] Growl: GRRRR

Adesso però facciamo la magia! usiamo due metodi dell’obj-c runtime per mischiare le carte 🙂

Primo passo importiamo la libreria con un:

#import

Poi aggiungiamo sotto il codice scritto poco fa il codice per scambiare le due implementazioni:

Method original, swizzled;
original = class_getInstanceMethod([Dog class], @selector(bark));
swizzled = class_getInstanceMethod([Dog class], @selector(growl));
method_exchangeImplementations(original, swizzled);

a questo punto possiamo ri-verifica il comportamento della nostra classe, stampando nuovamente le righe di log. Ecco il codice completo della mia funzione main():

#import 
#import 
#import "Dog.h"

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Dog *dog1 = [[Dog alloc] init];
        NSLog(@"Bark: %@", [dog1 bark]);
        NSLog(@"Growl: %@", [dog1 growl]);

        Method original, swizzled;
        original = class_getInstanceMethod([Dog class], @selector(bark));
        swizzled = class_getInstanceMethod([Dog class], @selector(growl));
        method_exchangeImplementations(original, swizzled);

        NSLog(@"Bark: %@", [dog1 bark]);
        NSLog(@"Growl: %@", [dog1 growl]);
    }
    return 0;
}

Adesso l’output sarà questo:

2013-09-07 09:52:40.646 learnSwizzling[95797:303] Bark: WOOF
2013-09-07 09:52:40.649 learnSwizzling[95797:303] Growl: GRRRR
2013-09-07 09:52:40.650 learnSwizzling[95797:303] Bark: GRRRR
2013-09-07 09:52:40.651 learnSwizzling[95797:303] Growl: WOOF

dove si nota chiaramente che le ultime due invocazioni hanno cambiato il loro comportamento.

Conclusioni

Conoscere e utilizzare in modo produttivo l’obj-c runtime può aiutare molto nello sviluppo di applicazioni complesse, ma occhio a come viene usato! Invertire l’implementazione di alcuni metodi o agire così in profondità può portare ad avere un codice dal comportamento imprevedibile e quindi impossibile da debuggare, quindi usatelo solo quando strettamente necessario.

Chi conosce l’argomento UnitTest (qui una introduzione) probabilmente utilizza anche qualche framework per gestire i mock object, come OCMock oppure OCMokito. Alla base del funzionamento di questi framework c’è proprio l’obj-c runtime senza il quale non si potrebbero compiere tutte le modifiche a basso livello necessarie per generare i mock object.

Per chi volesse approfondire l’argomento del method swizzling consiglio la lettura di questo post: http://stackoverflow.com/a/8636521

 

 

Alla prossima!

Share this story:
  • tweet

Tags: binding dinamicoobj-c developmentObjective-cruntime objective-c

Recent Posts

  • Parte il percorso programmatori iOS in Swift su devACADEMY.it

    20 Dicembre 2017 - 0 Comment
  • Android, crittografare dati velocemente con Encryption

    24 Settembre 2018 - 0 Comment
  • Sql2o, accesso immediato ai database tramite Java

    3 Settembre 2018 - 0 Comment
  • Okio, libreria per ottimizzare l’input/output in Java

    27 Agosto 2018 - 0 Comment

Related Posts

  • Andrea Picchi: Programmare con Objective-C 2.0 per iOS e OS X

    27 Febbraio 2013 - 10 Comments
  • T#107 – Le referenze associative: Aggiungiamo le variabili alle Categorie

    2 Aprile 2012 - 3 Comments
  • 3. Objective-C: Le basi – parte 2

    21 Luglio 2011 - 2 Comments

Author Description

No Responses to “Uno sguardo al runtime Objective-c”

Leave a Reply

Your email address will not be published. Required fields are marked *


*
*

Corso online di programmazione android e java

SEZIONI

  • Android
  • Comunicazioni
  • Contest
  • Corsi ed Eventi
  • Corso completo di C
  • Corso programmazione videogiochi
  • Framework
  • Grafica e Design
  • Guida rapida alla programmazione Cocoa Touch
  • Guide Teoriche
  • Guide varie
  • iPad
  • Le nostre applicazioni
  • Libri e manuali
  • Materiale OpenSource
  • News
  • Pillole di C++
  • Progetti completi
  • Risorse utili
  • Strumenti di Sviluppo
  • Swift
  • Tips & Tricks
  • Tutorial Pratici
  • Video Tutorial
  • Windows Phone

Siti Amici

  • Adrirobot
  • Allmobileworld
  • Apple Notizie
  • Apple Tribù
  • Avvocato360
  • Blog informatico 360°
  • bubi devs
  • fotogriPhone
  • GiovaTech
  • iApp-Mac
  • iOS Developer Program
  • iPodMania
  • MelaRumors
  • Meritocracy
  • SoloTablet
  • TecnoUser
  • Privacy & Cookie Policy
©2009-2018 devAPP - All Rights Reserved | Contattaci
devAPP.it è un progetto di DEVAPP S.R.L. - Web & Mobile Agency di Torino
Str. Volpiano, 54 - 10040 Leini (TO) - C.F. e P.IVA 11263180017 - REA TO1199665 - Cap. Soc. € 10.000,00 i.v.

devACADEMY.it

Vuoi imparare a programmare?

Iscriviti e accedi a TUTTI i corsi con un’unica iscrizione.
Oltre 70 corsi e migliaia di videolezioni online e in italiano a tua disposizione.

ISCRIVITI SUBITO