{"id":10741,"date":"2013-09-10T15:24:52","date_gmt":"2013-09-10T13:24:52","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=10741"},"modified":"2013-09-10T15:24:52","modified_gmt":"2013-09-10T13:24:52","slug":"uno-sguardo-al-runtime-objective-c","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/uno-sguardo-al-runtime-objective-c\/","title":{"rendered":"Uno sguardo al runtime Objective-c"},"content":{"rendered":"<p>Quando si parla di linguaggio di programmazione Objective-C, abbreviato spesso con Obj-c, una tra le prime informazioni che viene data \u00e8 che l&#8217;intero linguaggio \u00e8 un superset del linguaggio standard C. Ci\u00f2 significa in breve che all&#8217;interno di un programma obj-c potete scrivere tutto il codice C che vi pare, questo funzioner\u00e0 perfettamente. Questo permette, tra l&#8217;altro, di poter riutilizzare moltissime librerie &#8220;classiche&#8221; scritte in C all&#8217;interno dei nostri programmi, come le socket BSD e molto altro.<\/p>\n<p>Obj-C ha un&#8217;altra particolarit\u00e0 degna di nota, che va sotto il nome di Binding Dinamico (<a href=\"http:\/\/it.wikipedia.org\/wiki\/Binding\" target=\"_blank\">wikipedia<\/a>). Il Binding \u00e8 il processo che viene eseguito dal compilatore o dall&#8217;interprete che associa una particolare parte di codice al nome di una funzione. \u00c8, in altri termini, quel processo che ci permette di invocare una parte di codice utilizzando il nome della funzione associata.<\/p>\n<p>In Obj-C questo processo viene eseguito a <em>runtime<\/em>, viene cio\u00e8 fatto durante l&#8217;esecuzione del programma stesso e prende il nome di <i>Binding Dinamico<\/i>, mentre negli altri linguaggi viene effettuato a\u00a0<em>compile time<\/em>, ovvero durante la fase di compilazione. In quest&#8217;ultimo caso si chiama\u00a0<em>Binding statico.<\/em><!--more--><\/p>\n<h2>Il binding in C<\/h2>\n<p>Il binding \u00e8 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: <a href=\"http:\/\/ideone.com\/l4KIYx\" target=\"_blank\">http:\/\/ideone.com\/l4KIYx<\/a>)<\/p>\n<pre lang=\"C\">\r\n#include \r\n\r\nint doppio(int n) {\r\n\treturn n * 2;\r\n}\r\n\r\nint main(void) {\r\n\tint input = 42;\r\n\tint risultato = doppio(input);\r\n\r\n\tprintf(\"Il risultato: %d\", risultato);\r\n\treturn 0;\r\n}<\/pre>\n<p>In questo caso il compilatore &#8220;semplicemente&#8221; sostituisce alla chiamata alla funzione doppio() il suo indirizzo di memoria.<\/p>\n<p>In questo secondo esempio abbiamo invece un puntatore a funzione, che viene assegnato alla funzione doppio() o triplo() a seconda se il valore \u00e8 pari o dispari. (esegui il codice qui: <a href=\"http:\/\/ideone.com\/IkYy9B\" target=\"_blank\">http:\/\/ideone.com\/IkYy9B<\/a>)<\/p>\n<pre lang=\"C\">#include \r\n\r\nint doppio(int n) {\r\n\treturn n * 2;\r\n}\r\nint triplo (int n) {\r\n\treturn n * 3;\r\n}\r\n\r\nint main(void) {\r\n\tint k = 3;\r\n\tint risultato;\r\n\r\n\tint (*func)(int k);\r\n\r\n\tif (k % 2 == 0) {\r\n\t\tfunc = doppio;\r\n\t} else {\r\n\t\tfunc = triplo;\r\n\t}\r\n\r\n\trisultato = func(k);\r\n\tprintf(\"Il risultato: %d\", risultato);\r\n\r\n\treturn 0;\r\n}<\/pre>\n<p>Quello che \u00a0si vede in questo esempio \u00e8 che usando i puntatori a funzione possiamo, a\u00a0<em>runtime<\/em>, switchare il comportamento della funzione tra una implementazione o un&#8217;altra. Possiamo, cio\u00e8, con lo stesso nome invocare parti di codice completamente distinte.<\/p>\n<p>Questo ragionamento un po&#8217; articolato \u00e8 indispensabile se si vuole scrivere codice ad oggetti con un linguaggio che nativamente non \u00e8 ad oggetti ed \u00e8 infatti alla base del funzionamento dell&#8217;Obj-c.<\/p>\n<p>In Obj-c tutta la parte che si occupa del binding \u00e8 inserita in una libreria che si chiama &#8220;Objective-C runtime&#8221; e della quale potete leggere qui (<a href=\"http:\/\/goo.gl\/Tc2y3j\" target=\"_blank\">http:\/\/goo.gl\/Tc2y3j<\/a>) la documentazione e qui (<a href=\"http:\/\/goo.gl\/fd1U6A\" target=\"_blank\">http:\/\/goo.gl\/fd1U6A<\/a>) la programming guide.<\/p>\n<p>Questa libreria \u00e8 molto importante anche per altri argomenti che vanno oltre lo scopo di questo articolo, come l&#8217;introspection e la metaprogrammazione.<\/p>\n<h2>Objc_msgsend<\/h2>\n<p>Cosa succede dietro le quinte del linguaggio Obj-C quando viene inviato un messaggio? Cosa fa di preciso una semplice riga come questa?<\/p>\n<pre lang=\"Obj-c\">\r\nid returnvalue = [anobject messageName:parameter];\r\n<\/pre>\n<p>Il compilatore traduce questa riga in puro C e traducendo l&#8217;invio del messaggio in una chiamata alla funzione objc_msgSend() il cui prototipo \u00e8 questo:<\/p>\n<pre>\r\nid objc_msgSend(id theReceiver, SEL theSelector, ...)\r\n<\/pre>\n<p>Questa &#8220;traduzione&#8221; produce questa riga di codice<\/p>\n<pre lang=\"C\">\r\nid returnvalue = objc_msgsend(anobject, @selector(messageName:), parameter);\r\n<\/pre>\n<p>Per trovare la corretta implementazione del selector messageName l&#8217;obj-c runtime analizza la mappa dei metodi dell&#8217;oggetto anobject. Questa mappa contiene da una parte il selector (che \u00e8 una struct C) e dall&#8217;altra un puntatore a funzione.<br \/>\nSe il runtime non trova il selector all&#8217;interno di questa mappa allora segue il puntatore &#8220;isa&#8221; dell&#8217;oggetto per risalire nella gerarchia delle classi.<br \/>\nSe per esempio invochiamo il metodo alloc sulla classe NSString, nella sua mappa non ci sar\u00e0 alcuna entry per questo selector e in runtime proseguir\u00e0 nella ricerca sulla classe NSObject che \u00e8 l&#8217;immediata superclass di NSString.<\/p>\n<h2>Swizzling<\/h2>\n<p>La tecnica dello swizzling constite nell&#8217;invertire a runtime l&#8217;implementazione di due metodi ed \u00e8 quindi possibile soltanto negli ambienti con il binding dinamico.<br \/>\nObj-c si presta bene a questo tipo di tecnica che, anche se \u00e8 un po&#8217; &#8220;oscura&#8221;, si pu\u00f2 rivelare molto utile in certi casi.<\/p>\n<p>Create un progetto xcode di qualsiasi tipo, in questo caso io ho utilizzato &#8220;console application&#8221; perch\u00e9 non ho bisogno di alcuna interfaccia per questo esercizio.<\/p>\n<p>All&#8217;interno del progetto create una classe con due metodi:<\/p>\n<pre lang=\"Obj-c\">\r\n\/\/file Dog.h\r\n#import \r\n\r\n@interface Dog : NSObject\r\n-(NSString *)bark;\r\n-(NSString *)growl;\r\n@end\r\n\r\n\/\/file Dog.m\r\n#import \"Dog.h\"\r\n\r\n@implementation Dog\r\n-(NSString *)bark {\r\n    return @\"WOOF\";\r\n}\r\n\r\n-(NSString *)growl {\r\n    return @\"GRRRR\";\r\n}\r\n@end\r\n<\/pre>\n<p>Se avete creato un progetto di tipo console andate a modificare il file main, viceversa utilizzate il vostro delegate.<br \/>\nLa prima cosa da fare \u00e8 verificare il comportamento della nostra classe, quindi scriviamo un piccolo esempio all&#8217;interno del main<\/p>\n<pre lang=\"Obj-c\">\r\nint main(int argc, const char * argv[])\r\n{\r\n\r\n    @autoreleasepool {\r\n        Dog *dog1 = [[Dog alloc] init];\r\n        NSLog(@\"Bark: %@\", [dog1 bark]);\r\n        NSLog(@\"Growl: %@\", [dog1 growl]);\r\n}\r\n}\r\n<\/pre>\n<p>l&#8217;output sar\u00e0 senza grosse sorprese:<\/p>\n<pre>\r\n2013-09-07 09:52:40.646 learnSwizzling[95797:303] Bark: WOOF\r\n2013-09-07 09:52:40.649 learnSwizzling[95797:303] Growl: GRRRR\r\n<\/pre>\n<p>Adesso per\u00f2 facciamo la magia! usiamo due metodi dell&#8217;obj-c runtime per mischiare le carte \ud83d\ude42<\/p>\n<p>Primo passo importiamo la libreria con un:<\/p>\n<pre lang=\"Obj-c\">#import<\/pre>\n<p>Poi aggiungiamo sotto il codice scritto poco fa il codice per scambiare le due implementazioni:<\/p>\n<pre lang=\"Obj-c\">\r\nMethod original, swizzled;\r\noriginal = class_getInstanceMethod([Dog class], @selector(bark));\r\nswizzled = class_getInstanceMethod([Dog class], @selector(growl));\r\nmethod_exchangeImplementations(original, swizzled);\r\n<\/pre>\n<p>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():<\/p>\n<pre lang=\"obj-c\">\r\n#import \r\n#import \r\n#import \"Dog.h\"\r\n\r\nint main(int argc, const char * argv[])\r\n{\r\n\r\n    @autoreleasepool {\r\n        Dog *dog1 = [[Dog alloc] init];\r\n        NSLog(@\"Bark: %@\", [dog1 bark]);\r\n        NSLog(@\"Growl: %@\", [dog1 growl]);\r\n\r\n        Method original, swizzled;\r\n        original = class_getInstanceMethod([Dog class], @selector(bark));\r\n        swizzled = class_getInstanceMethod([Dog class], @selector(growl));\r\n        method_exchangeImplementations(original, swizzled);\r\n\r\n        NSLog(@\"Bark: %@\", [dog1 bark]);\r\n        NSLog(@\"Growl: %@\", [dog1 growl]);\r\n    }\r\n    return 0;\r\n}\r\n<\/pre>\n<p>Adesso l&#8217;output sar\u00e0 questo:<\/p>\n<pre>\r\n2013-09-07 09:52:40.646 learnSwizzling[95797:303] Bark: WOOF\r\n2013-09-07 09:52:40.649 learnSwizzling[95797:303] Growl: GRRRR\r\n2013-09-07 09:52:40.650 learnSwizzling[95797:303] Bark: GRRRR\r\n2013-09-07 09:52:40.651 learnSwizzling[95797:303] Growl: WOOF\r\n<\/pre>\n<p>dove si nota chiaramente che le ultime due invocazioni hanno cambiato il loro comportamento.<\/p>\n<h2>Conclusioni<\/h2>\n<p>Conoscere e utilizzare in modo produttivo l&#8217;obj-c runtime pu\u00f2 aiutare molto nello sviluppo di applicazioni complesse, ma occhio a come viene usato! Invertire l&#8217;implementazione di alcuni metodi o agire cos\u00ec in profondit\u00e0 pu\u00f2 portare ad avere un codice dal comportamento imprevedibile e quindi impossibile da debuggare, quindi usatelo solo quando strettamente necessario.<\/p>\n<p>Chi conosce l&#8217;argomento UnitTest (<a href=\"http:\/\/goo.gl\/hTzmG\" target=\"_blank\">qui<\/a>\u00a0una introduzione) probabilmente utilizza anche qualche framework per gestire i mock object, come <a href=\"http:\/\/ocmock.org\/\" target=\"_blank\">OCMock<\/a> oppure <a href=\"https:\/\/github.com\/jonreid\/OCMockito\" target=\"_blank\">OCMokito<\/a>. Alla base del funzionamento di questi framework c&#8217;\u00e8 proprio l&#8217;obj-c runtime senza il quale non si potrebbero compiere tutte le modifiche a basso livello necessarie per generare i mock object.<\/p>\n<p>Per chi volesse approfondire l&#8217;argomento del method swizzling consiglio la lettura di questo post:\u00a0<a href=\"http:\/\/stackoverflow.com\/a\/8636521\" target=\"_blank\">http:\/\/stackoverflow.com\/a\/8636521<\/a><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>Alla prossima!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Quando si parla di linguaggio di programmazione Objective-C, abbreviato spesso con Obj-c, una tra le prime informazioni&#8230;<\/p>\n","protected":false},"author":53,"featured_media":10758,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[1320,1321,5,1319],"class_list":["post-10741","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-guide-teoriche","tag-binding-dinamico","tag-obj-c-development","tag-objective-c","tag-runtime-objective-c"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/10741","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/53"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=10741"}],"version-history":[{"count":16,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/10741\/revisions"}],"predecessor-version":[{"id":10760,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/10741\/revisions\/10760"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media\/10758"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=10741"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=10741"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=10741"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}