Articoli con Tag ‘Apple’


Come impostare XCode per usare l’iPhone al posto del simulatore

Screencast

Continua...

iPhone: creare un metodo per rispondere agli eventi cross class

Normalmente un evento, che altro non è che un messaggio, viene risolto (impostato ed implementato) nella stessa classe o contesto, della funzione o procedura “chiamante”. Ad esempio se aggiungiamo un bottone UIButton via codice (programmatically), possiamo trovarci all’interno di una nostra classe UIView o in un UIViewController. In entrambi i casi l’operazione di allocazione e inizializzazione del bottone sarà seguita dall’impostazione del target che dovrà ricevere un messaggio quando si “cliccherà” sul bottone, sul tipo:

1
2
3
4
5
6
7
8
9
UIButton *bottone = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bottone.frame = CGRectMake(10, 180, 300, 30);
[bottone setTitle:@"Press me" forState:UIControlStateNormal];
// decide chi deve ricevere il messaggio UIControlEventTouchUpInside
[bottone addTarget:self action:@selector(onButtonClicked) forControlEvents:UIControlEventTouchUpInside];
// ...
- (void) onButtonClicked {
// ...
}

La riga 5 del codice mostrato sopra decide chi (l’oggetto) e cosa (il metodo) “chiamare” quando il nostro bottone è stato premuto. Nell’esempio sopra illustrato si nota anche che l’impostazione del messaggio di pressione viene inviato al metodo onButtonClick implementato più sotto, quindi facente parte dello stesso contesto (o classe). La prima considerazione ovvia che possiamo fare, dunque, è che alterando i parametri self e action potremmo inviare il nostro messaggio ad un qualsiasi altro oggetto, posto quindi al difuori del contesto in uso. Ecco un esempio pratico: una classe UIApplicationDelegate crea un UIViewController:

1
2
3
4
5
//
// myAppDelegate.m
//
splashScreenController = [SplashScreenController alloc];
[window addSubview:splashScreenController.view];

Lo SplashScreenController espone un metodo che permette di animare la UIView associata al UIViewController stesso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
// SplashScreenController.m
//
- (void) animateBackgroundDown {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.75];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(onAnimationFinished)];
    self.view.frame = (CGRect){0,480,320,480};
    [UIView commitAnimations];
}
// ...
- (void) onAnimationFinished {
    NSLog(@"Animazione terminata");
}

Il codice sopra mostra un metodo definito all’interno della classe SplashScreenController di tipo UIViewController. Esso non fa altro che animare la UIView animandola verso il basso e, quando l’animazione è terminata, chiamare il metodo (invia un messaggio a se stesso – da qui self) onAnimationFinished definito più sotto, facente parte sempre della classe SplashScreenController. Ne segue che nella nostra myAppDelegate, quando invocheremo il metodo animateBackgroundDown, non saremo informati della fine dell’animazione:

1
2
3
4
//
// myAppDelegate.m
//
[splashScreenController animateBackgroundDown];

Quello che potremmo desiderare, invece, è realizzare una nuova versione di animateBackgroundDown tale da dirgli a chi inviare il messaggio di fine animazione e quale metodo chiamare. In pratica vogliamo fare in modo di poter scrivere nella nostra classe myAppDelegate:

1
2
3
4
5
6
7
8
//
// myAppDelegate.m
//
[splashScreenController animateBackgroundDown:self selector:@selector(onAnimationFinished)];
// ...
- (void) onAnimationFinished {
    NSLog(@"Animazione terminata");
}

Questa volta il metodo onAnimationFinished non si trova nel UIViewController, bensì in myAppDelegate. Per fare questo è sufficiente modificare il metodo animateBackgroundDown nel UIViewController nel modo seguente:

1
2
3
4
5
6
7
8
9
10
11
12
//
// SplashScreenController.m
//
- (void) animateBackgroundDown:(id)target selector:(SEL)selector {
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.75];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDelegate:target];
    [UIView setAnimationDidStopSelector:selector];
    self.view.frame = (CGRect){0,480,320,480};
    [UIView commitAnimations];
}

Adesso abbiamo un metodo che accetta il “contesto” (target) e il metodo da chiamare (selector). Adesso, quando l’animazione termina, il messaggio AnimationDidStop sarà inviato a myAppDelegate a qualsiasi altro “oggetto” / classe in grado di riceverlo.

Continua...

Objective-C: un’alternativa all’uso di CGRectMake

CGRectMake() è una funzione (in realtà è un inline #define) molto usata soprattutto quando si creano da codice oggetti grafici o di interfaccia utente. Il suo utilizzo è quindi spesso legato all’inizializzazione di componenti UIKit, ma anche a semplici UIView o UIImageView. CGRectMake() restituisce una struct (struttura di tipo) CGRect:

1
2
3
4
5
struct CGRect {
  CGPoint origin;
  CGSize size;
};
typedef struct CGRect CGRect;

Che a sua volta è composta da due diverse struct CGPoint e CGSize:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct CGPoint {
  CGFloat x;
  CGFloat y;
};
typedef struct CGPoint CGPoint;

/* Sizes. */

struct CGSize {
  CGFloat width;
  CGFloat height;
};
typedef struct CGSize CGSize;

Che, a loro volta ancora, contengono tipi CGFloat, ovvero tipi float. Se andiamo ad analizzare il codice della CGRectMake() troviamo:

1
2
3
4
5
6
7
8
CG_INLINE CGRect
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
  CGRect rect;
  rect.origin.x = x; rect.origin.y = y;
  rect.size.width = width; rect.size.height = height;
  return rect;
}

Ne deriva, quindi, che questa parte di codice:

1
2
3
4
UIButton *gbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
gbutton.frame = CGRectMake (12,409,100,40);
[gbutton setTitle:@"Press" forState:UIControlStateNormal];
[mainWindow addSubview:gbutton];

Potrebbe, a ragione, essere scritta anche come:

1
2
3
4
UIButton *gbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
gbutton.frame = (CGRect) {12,409,100,40};
[gbutton setTitle:@"Press" forState:UIControlStateNormal];
[mainWindow addSubview:gbutton];

Giusto per velocizzare un po’ l’esecuzione del codice… :)

Continua...

Xcode shortcut

Xcode è davvero un ottimo ambiente di sviluppo, curato e denso di dettagli che rendono la scrittura di codice efficiente e piacevole. Tra questi c’è sicuramente la comodità del completamento automatico durante la digitazione, soprattutto quando si scrivono applicazioni per Apple iPhone, dove i framework sono tanti e ricordarsi sintassi e nomenclatura è impresa da pochi.

Continua...

XCode: consigli sul Debugging Preferences

Tramite le Preferences di Xcode è possibile impostare il comportamento dell’ambiente durante la fase di debugging di un’applicazione iPhone. Le impostazioni predefinite, infatti, sono assai scomode quando si prova e riprova un’applicazione; ad esempio, dopo aver lanciato la nostra applicazione, bisogna aprire manualmente la finestra Console per vedere l’output dei vari NSLog(). Inoltre Xcode lascia le precedenti sessioni così da costringerci a ripulire la finestra a mano. Fortunatamente è possibile risolvere il problema agendo sulle Preferences:

xcode-preferences

Come mostrato nella figura qui sopra, basta selezionare una delle voci del menu On Start per decide quale finestra di debug aprire in automatico all’avvio della nostra appicazione (io ho impostato Console & Debugger ma potete scegliere quelle che più vi fa comodo). Sulla destra poi troviamo Auto Clear Debug Console, così da partire sempre con la Console pulita.

Continua...

iPhone SecondApp: indovina il numero – parte 2

Come anticipato in iPhone FirstApp: indovina il numero – parte 1 vediamo come realizzare un’applicazione per Apple iPhone senza usare Interface Builder! Anzi, elimineremo fisicamente i file di Interface Builder creati dal wizard di Xcode. Alla fine di questo post, quindi, avremo un’applicazione identica, in tutto e per tutto, a quella realizzata nella parte prima, con la differenza che realizzeremo tutti i nostri componenti visivi, compresa la Window principale, completamente da codice.

L’applicazione già fatta, se volete solo scaricarla, è disponibile sul mio Google Code repository:

Vorrei far notare da subito come lo ZIP di questo esempio pesi meno di quello della scorsa volta! :)

Creiamo il progetto

Iniziamo creando il nostro progetto SecondApp (per distinguerlo da FirstApp), anche se questa volta sceglieremo Window-based Application:

newproject

Adesso andiamo ad eliminare tutto ciò che riguarda Interface Builder. Eliminiamo il file MainWindow.xib, situato nella cartella Resources: eliminatelo anche dal file system, quindi selezionate Also Move to Trash. Selezionate, poi, il file SecondoApp-info.plist ed eliminate il riferimento alla MainWindow nella casella Main nib file base name:

deleteib

A questo punto non abbiamo più nessuna Window, almeno tramite Interface Builder. Quindi apriamo il file main.m, situato in Other Sources, e modifichiamo la funzione main() in questo modo:

1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char *argv[]) {
   
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
    // avendo eliminato il file .xib abbiamo perso il puntamento
    // all'app delegate, quindi lo passiamo a "mano"
    int retVal = UIApplicationMain(argc, argv, nil, @"SecondAppAppDelegate");

    [pool release];
    return retVal;
}

Apriamo quindi SecondAppAppDelegate.m e creiamo la Window principale via codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)applicationDidFinishLaunching:(UIApplication *)application {    

    // ottiene le dimensioni delle schermo (320, 480)
    CGRect windowRect = [[UIScreen mainScreen] applicationFrame];
    // creiamo una finestra - visto che abbiamo fatto bye bye a Interface Builder
    UIWindow *mainWindow = [[UIWindow alloc] initWithFrame:windowRect];
    // impostiamo il background della Window a giallo, per differenziarlo
    // dalla precedente applicazione FirstApp
    [mainWindow setBackgroundColor:[UIColor yellowColor]];
 
    [self setWindow:mainWindow];
    [window makeKeyAndVisible];
 
    [mainWindow release];
}

Potete già provare l’applicazione; se appare una window gialla avete fatto tutto bene!

Nel file SecondAppAppDelegate.h possiamo eliminare IBOutlet, necessario solo se si usa Interface Builder. Inoltre aggiungiamo qui le nostre variabili globali che, nella scorsa volta, avevamo inserito nel Controller. Modifichiamo quindi il file SecondAppAppDelegate.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import <UIKit/UIKit.h>

@interface SecondAppAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
 
    UITextField *numero;
    UIButton *bottone;
    int numeroACaso;
}

@property (nonatomic, retain) UIWindow *window;

- (void)controllaNumero;

@end

Anche in questo caso abbiamo preparato la definizione del metodo controllaNumero, come la scorsa volta, ma abbiamo eliminato l’indicazione IBAction, in quanto non stiamo usando Interface Builder.

Costruiamo l’interfaccia da codice

E’ giunto il momento di creare tramite codice tutti i componenti della nostra interfaccia. Torniamo nel file SecondAppAppDelegate.m, posizioniamoci prima del [mainWindow release] ed inseriamo il seguente codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// crea la barra del titolo
UINavigationBar *myNavigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
myNavigationBar.barStyle = UIBarStyleDefault;
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Indovina un numero"];
[myNavigationBar pushNavigationItem:navigationItem animated:NO];
[window addSubview:myNavigationBar];
 
// crea la label
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 50, 300, 80)];
myLabel.backgroundColor = [UIColor clearColor];
myLabel.numberOfLines = 2;
myLabel.text = @"iPhone ha pensato un numero da 1 a 10, prova ad indovinarlo?";
[window addSubview:myLabel];
 
// crea il text input
numero = [[UITextField alloc] initWithFrame:CGRectMake(10, 120, 300, 30)];
numero.borderStyle = UITextBorderStyleRoundedRect;
numero.textAlignment = UITextAlignmentCenter;
numero.keyboardType = UIKeyboardTypeNumberPad;
numero.placeholder = @"Inserisci il numero";
[window addSubview:numero];
 
// crea il bottone
bottone = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bottone.frame = CGRectMake(10, 180, 300, 30);
[bottone setTitle:@"Premi qui" forState:UIControlStateNormal];
[bottone addTarget:self action:@selector(controllaNumero) forControlEvents:UIControlEventTouchUpInside];
[window addSubview:bottone];
 
[myLabel release];
[navigationItem release];
[myNavigationBar release];

Dato che l’applicationDidFinishLaunching corrisponde alla viewDidLoad della scorsa volta, subito dopo il [mainWindow release]; inseriamo:

1
numeroACaso = 1 + arc4random() % 10;

Ora non ci rimane che implementare il metodo controllaNumero, che sarà identico (a parte il prototipo) a quello usato la scorsa volta:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void) controllaNumero {
    NSLog(@"Premuto bottone di controlla numero");
    int numeroInserito = [numero.text integerValue];
    NSString *message;
    NSLog(@"Il numero inserito è %d", numeroInserito );
    if( numeroInserito <numeroACaso ) {
        message = @"Troppo basso...";
    } else if (numeroInserito> numeroACaso ) {
        message = @"Troppo alto...";
    } else if (numeroInserito == numeroACaso ) {
        message = @"Bravo hai indovinato";
        numeroACaso = 1 + arc4random() % 10;
        NSLog(@"Numero pensato %d", numeroACaso);
    }
    UIAlertView *alertMessaggio = [[UIAlertView alloc]
                initWithTitle:@"Responso"
                message: message
                delegate:nil
                cancelButtonTitle:@"OK"
                otherButtonTitles:nil];
    [alertMessaggio show];
    [alertMessaggio release];
    numero.text = @"";
}

Abbiamo finito!

Conclusioni e considerazioni

Questo esempio non fa uso diretto di una UIView o di un UIViewController, proprio perchè volevo lasciarlo il più semplice e snello possibile e, anche, per dimostrare che non sono elementi sempre necessari. Tuttavia inserire gli oggetti direttamente nella window può avere un qualche senso in questo esempio ed in altri sporadici contesti. L’uso delle UIView e dei UIViewController portano comunque benefici in tantissimi altri casi, ein alcuni sono praticamente indispensabili; come avremo modo di vedere in futuro.

Continua...

XCode: organizzare il codice con la direttiva #pragma mark

XCode è uno strumento davvero potente e versatile e fornisce al programmatore tantissime features utili e semplici da usare. Nella stesura di codice complesso, o quantomeno articolato, diventa importante organizzare il nostro codice in modo da non perdere tempo a cercare funzioni disseminate in lunghissime righe di codice. Oltre a commentare, prima e importante operazione da fare, l’ambiente XCode mette a disposizione delle direttive (simpatiche) per migliorare la legginilità e l’usabilità all’interno dell’ambiente di sviluppo. Una di queste è la direttiva #pragma mark che diventa utilissima nell’organizzare il codice e i gruppi di metodi.

Nell’immagine qui sotto è visibile la parte di codice del mio progetto PragmaTest:

pragmamark-1

Il menu dropdown in alto permette di elencare tutti i metodi della nostra classe. Ora se inseriamo sopra il nostro metodo la direttiva #pragma mark che ha una sintassi:

1
#pragma mark {label}

Otteniamo:

pragmamark-2

Il primo #pragma mark con il trattino (-) inserisce una riga di separazione. Il secondo è un testo (label) a piacere. Potete inserire la direttiva dove volete, organizzando il codice come meglio credete. Inoltre potete inserire anche più righe pragma, tipo:

1
2
3
4
5
6
#pragma mark -
#pragma mark /**
#pragma mark  * Uso della direttiva pragma
#pragma mark  * su più linee di codice
#pragma mark  */
- (void) mioMetodo {}

Continua...

Apple iPhone: The First Release 1.2

thefirst12claim

appstore-60

Disponibile su AppStore l’aggiornamento alla release 1.2 di TheFirst. Ecco le release notes:

  • Rivista completamente tutta la grafica del gioco, che ora è notevolmente più gradevole
  • Compatibilità con Apple iPhone 3.0
  • Aggiunti effetti sonori
  • Rivista e migliorata la navigabilità tra il gioco e le istruzioni

Continua...

iPhone: tutti i font di sistema

L’Apple iPhone mette a disposizione un numero limitato di Font agli sviluppatori. La lista dei font disponibili è facilmente ottenibile da codice, come vedremo. Se volete usare un vostro font, ad esempio includendolo nelle risorse, la cosa è un pochino più articolata e conivolge anche la questione delle licenze (diritti) sui font “embeddati”… ne riparleremo in seguito. Tornando invece ai font ufficiali presenti nell’Apple iPhone questi sono (cliccate sull’immagine per ingrandire):

iphonefonts

L’SDK di Apple iPhone permette di accedere a degli speciali font di sistema. Questi sono identificati da particolari costanti e sono:

1
2
3
UIFont *myBoldFont   = [UIFont boldSystemFontOfSize:12.0];
UIFont *mySystemFont = [UIFont SystemFontOfSize:12.0];
UIFont *myItalicFont = [UIFont italicSystemFontOfSize:12.0];

Se volete invece ottenere un puntatore ad uno odei font mostrati nell’immagine di sopra basta usare:

1
UIFont *myCustomFont = [UIFont fontWithName:@"Helvetica-Bold" size:22.0];

Come avrete notato la gestione del font è particolare; oltre alla famiglia (Helvetica, Courier, etc…) bisogna specificare il tipo (bold, italic, etc…). In pratica, quindi, un font deve essere fornito di queste caratteristiche. L’Helvetica, ad esempio, è presente con:

1
2
3
4
Helvetica
Helvetica-Bold
Helvetica-Oblique
Helvetica-BoldOblique

Se volete visualizzare direttamente i vostri font sull’iPhone, ecco qualche linea di codice utile:

1
2
3
4
5
6
7
8
9
10
11
12
NSArray *listOfFonts = [[NSArray alloc] initWithArray:[UIFont familyNames]];
NSArray *subFontTypes;

for (int i=0; i<[listOfFonts count]; i++) {
    NSLog(@"Font Family: %@", [listOfFonts objectAtIndex:i]);
    subFontTypes = [[NSArray alloc] initWithArray:[UIFont fontNamesForFamilyName:[listOfFonts objectAtIndex:i]]];
    for (int j=0; j<[subFontTypes count]; j++) {
        NSLog(@"+----->Type: %@", [subFontTypes objectAtIndex:j]);
    }
    [subFontTypes release];
}
[listOfFonts release];

Con le SDK 2.2.1, sul mio simulatore, ho ottenuto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Font Family: Courier
+----->Type: Courier
+----->Type: Courier-BoldOblique
+----->Type: Courier-Oblique
+----->Type: Courier-Bold
Font Family: AppleGothic
+----->Type: AppleGothic
Font Family: Arial
+----->Type: ArialMT
+----->Type: Arial-BoldMT
+----->Type: Arial-BoldItalicMT
+----->Type: Arial-ItalicMT
Font Family: STHeiti TC
+----->Type: STHeitiTC-Light
+----->Type: STHeitiTC-Medium
Font Family: Hiragino Kaku Gothic ProN
+----->Type: HiraKakuProN-W6
+----->Type: HiraKakuProN-W3
Font Family: Courier New
+----->Type: CourierNewPS-BoldMT
+----->Type: CourierNewPS-ItalicMT
+----->Type: CourierNewPS-BoldItalicMT
+----->Type: CourierNewPSMT
Font Family: Zapfino
+----->Type: Zapfino
Font Family: Arial Unicode MS
+----->Type: ArialUnicodeMS
Font Family: STHeiti SC
+----->Type: STHeitiSC-Medium
+----->Type: STHeitiSC-Light
Font Family: American Typewriter
+----->Type: AmericanTypewriter
+----->Type: AmericanTypewriter-Bold
Font Family: Helvetica
+----->Type: Helvetica-Oblique
+----->Type: Helvetica-BoldOblique
+----->Type: Helvetica
+----->Type: Helvetica-Bold
Font Family: Marker Felt
+----->Type: MarkerFelt-Thin
Font Family: Helvetica Neue
+----->Type: HelveticaNeue
+----->Type: HelveticaNeue-Bold
Font Family: DB LCD Temp
+----->Type: DBLCDTempBlack
Font Family: Verdana
+----->Type: Verdana-Bold
+----->Type: Verdana-BoldItalic
+----->Type: Verdana
+----->Type: Verdana-Italic
Font Family: Times New Roman
+----->Type: TimesNewRomanPSMT
+----->Type: TimesNewRomanPS-BoldMT
+----->Type: TimesNewRomanPS-BoldItalicMT
+----->Type: TimesNewRomanPS-ItalicMT
Font Family: Georgia
+----->Type: Georgia-Bold
+----->Type: Georgia
+----->Type: Georgia-BoldItalic
+----->Type: Georgia-Italic
Font Family: STHeiti J
+----->Type: STHeitiJ-Medium
+----->Type: STHeitiJ-Light
Font Family: Arial Rounded MT Bold
+----->Type: ArialRoundedMTBold
Font Family: Trebuchet MS
+----->Type: TrebuchetMS-Italic
+----->Type: TrebuchetMS
+----->Type: Trebuchet-BoldItalic
+----->Type: TrebuchetMS-Bold
Font Family: STHeiti K
+----->Type: STHeitiK-Medium
+----->Type: STHeitiK-Light

Continua...

Come eliminare NSLog() dai sorgenti XCode

<a target="_blank" href="http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSLog">NSLog()</a> è una funzione utilissima durante le fasi iniziali di un progetto, per il testing e il debug di un’applicazione per Apple iPhone o, più in generale, in ambiente XCode. Essendo appunto una funzione, esattamente come le altre, la sua presenza si farà sentire anche quando rilasceremo (release) il nostro eseguibile. Diventa quindi necessario rimuovere, in qualche modo, tutte le righe di NSLog() dal nostro codice, sia perchè non più necessarie, sia perchè le chiamate a NSLog() potrebbero influire sulle performance della nostra applicazione, soprattutto se abbiamo inserito NSLog() all’interno di loop.

Escludiamo subito la soluzione del “cerca” ed “elimina”; perchè un domani ci potrebbero servire nuovamente. Escludiamo anche la soluzione del “cerca” e “commenta”, scomoda per lo stesso motivo di prima. Fortunatamente una soluzione pulita, semplice e corretta la troviamo sfruttando le istruzioni condizionali del compilatore. Quello che faremo, in pratica, e dire al compilatore di escludere – se si verifica una determinata condizione – durante la compilazione del nostro sorgente le righe che contengono NSLog().

Le direttive di compilazioni e le istruzioni condizionali del compilatore, sono uno strumento molto potente e diffuso. Chi proviene dallo sviluppo ANSI-C le conosce sicuramente molto bene e le avrà utilizzte in moltissime situazioni. La particolarità di queste “istruzioni” risiede nel fatto, sopra accennato, di essere viste dal compilatore e non dall’eseguibile. Questa loro caratteristica le rende utili in moltissimi casi e permette di risolvere problematiche altrimenti assai fastidiose.

Vediamo subito un esempio di codice che, come preannunciato, permette di “eliminare” dalla compilazione parti di codice, nel nostro caso NSLog():

1
2
3
4
5
6
#define ACTIVE_NSLOG 1
// se la costante ACTIVE_NSLOG è definita compila
// il blocco di codice compreso tra #ifdef e #endif
#ifdef ACTIVE_NSLOG
    NSLog( @" ... bla bla" );
#endif

Le istruzioni condizionali del compilatore fanno parte della stessa famiglia della #define; anch’esse, infatti, sono precedute dal carattere “cancelletto” (#). Nell’esempio mostrato abbiamo definito una costante ACTIVE_NSLOG; le successive righe di codice dicono al compilatore di “includere” la riga NSLog() solo se ACTIVE_NSLOG è definito. Se abbiamo avuto la cura, durante la stesura del nostro codice, di inserire le chiamate a NSLog() all’interno del blocco #ifdef ... #endif, basterà eliminare la definizione della costante ACTIVE_NSLOG per far sparire, alla prossima compilazione, tutti i nostri NSLog().

Una soluzione migliore e definitiva

Vediamo adesso come impostare l’ambiente XCode per migliorare ancor di più ciò che abbiamo fatto sopra! Prima di tutto scegliamo un nome di costante che useremo nei nostri progetti per escludere dalla compilazione NSLog(). Potete scegliere il nome che più vi piace, da DEBUG a MIO_DEBUG o quello che preferite. Aprite il vostro progetto, nuovo o vecchio. Inserite tutti gli NSLog() all’interno del blocco (o di un blocco):

1
2
3
#ifdef MIO_DEBUG
    NSLog( @" ... bla bla" );
#endif

Selezionate il file principale del vostro progetto, cliccate con il tasto destro e scegliete la voce Get Info.

getinfo

Si aprira la finestra con le informazioni sul progetto:

userdefine

Selezionate la scheda Build, verificate di essere in configurazione Debug (è questa la chicca), andate nella sezione User-Defined e aggiungete, tramite il bottone in basso a sinistra, un nuovo campo chiamato OTHER_CFLAGS. A questo assegnamoli il valore -DMIO_DEBUG=1. La sintassi è -D{mia define}=1.

Questa procedura ha due vantaggi:

  1. Non dobbiamo inserire nel codice la #define MIO_DEBUG 1, ma lo facciamo tramite le informazioni sul progetto. Quindi, quando andremo a compilare la versione di rilascio (quella senza gli NSLog()) non dobbiamo ricordarci di eliminare la riga #define MIO_DEBUG 1
  2. La costante è definita in relazione alla configurazione, nel nostro caso Debug. Quindi, passando alla configurazione di rilascio (release) la costante sarà assente e le righe con NSLog() non verranno compilate

Conclusioni

La procedura sopra descritta può essere utile in una moltidutine di altri casi che, con NSLog(), non hanno nulla a che fare. Le istruzioni condizionali del compilatore possono aiutarci in una vastissima gamma di contesti. Spesso sono utilizzate dai programmatori per determinare il tipo di sistema operativo, la versione, il target, la presenza di processori matematici, mantenendo uno stesso “identico” sorgente.

Per capirci, come esempio, possiamo utilizzre la nostra costante MIO_DEBUG anche per intervenire in altre zone del codice:

1
2
3
4
5
6
7
8
9
10
// se sono in debug vinco la partita
// con uno score di 100 invece che di 10000 :)
#ifdef MIO_DEBUG
    if( score == 100 )
#else
    if( score == 10000 )
#endif
    {
        [self haiVinto];
    }

Per completare, ecco alcuni esempi e varianti:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// più in generale esiste la
#if espressione
// simile alle if tradizionali, quindi con una espressione completa

// verifica se è definita una costante
#ifdef costante

// verifica se NON è definita una costante
#ifndef costante

// else
#else

// chiusura del blocco
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ad esempio...
#define DEBUG 1
#define MIA_ALTRA_COSTANTE 5
 
...
#if DEBUG
  // compila questo
#else
  // altrimenti compila quest'altro
#endif
 
#if MIA_ALTRA_COSTANTE > 4
  NSLog(@"...");
#endif
1
2
3
4
#ifndef INCLUDE_MIO_FILE
    #define INCLUDE_MIO_FILE
    #include "mio_file.h"
#endif

Continua...



Stop SOPA