[PPA 04] Streams de audio, PortAudio y un oscilador simple

PortAudio es una librería de audio I/O, multiplataforma y de código abierto. Permite escribir programas sencillos de audio en C o C++. Su objetivo es promover el intercambio de software de audio entre los desarrolladores en distintas plataformas. Muchas aplicaciones utilizan PortAudio para audio I/O.

El siguiente programa muestra una lista de las interfaces de audio
disponibles en el sistema:

Continue reading

(Español) [iOS 01] Introducción a Objective-C y el Foundation Framework.

Curso de Programación Para iOS

Agenda

  1. Introducción a Objective-C y el Foundation Framework.
  2. Programación para iOS, el UIKit Framework, Xcode e Interface Builder.
  3. Consumiendo Servicios Web.
  4. Aplicaciones Inmersivas.
  5. ¿?¿

Introducción a Objective-C y el Foundation Framework

1. Historia

Objective-C es un lenguaje de programación orientado a objetos creado como un super-conjunto de C pero que implementase un modelo de objetos parecido al de Smalltalk. Originalmente fue creado por Brad Cox y la corporación StepStone en 1980. En 1988 fue adoptado como lenguaje de programación de NEXTSTEP y en 1992 fue liberado bajo licencia GPL para el compilador GCC. Actualmente se usa como lenguaje principal de programación en Mac OS X y GNUstep.

Tomado de http://es.wikipedia.org/wiki/Objective-C

2. Objective-C es un super conjunto de C

Mejor dicho; todo lo que usted sabe de C sigue funcionando aquí:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
 Comentarios en bloque con un montón de carreta:

 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
 tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
 veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
 commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
 velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
 occaecat cupidatat non proident, sunt in culpa qui officia deserunt
 mollit anim id est laborum.
*/


// Comentarios de una sola línea.

// Declaraciones del preprocesador como #include, #ifdef, etc.
#include <stdio.h> // Las librerías estándar de C.

// Declaración de funciones:
float multiplique(float a, float b){
    // Operadores aritmeticos.
    return a*b;
}

// La función main que toma una lista de parámetros de la línea de comandos.
int main (int argc, const char * argv[])
{
    // Tipos de variables primitivos:
    int a = 2;
    float pi = 3.141592;
    char unCaracter = 'z';

    // printf y el resto de funciones estándar:
    printf("Los valores de estas variables:\na:%i pi:%f unCaracter:%c \n", a, pi, unCaracter);

    // Llamado a funciones.
    float resultado = multiplique(2.0, 3.0);

    // Arrays y strings:
    int losNumerosDelUnoAlDiez[10] = {1,2,3,4,5,6,7,8,9,10};

    float unArrayDeTresNumeros[3];
    unArrayDeTresNumeros[0] = 1.23;
    unArrayDeTresNumeros[1] = pi;
    unArrayDeTresNumeros[2] = -10;

    char* lasLetrasDeLaAaLaF = "A B C D E F";

    // Condicionales:
    int seraQueSi = 0;
    if(seraQueSi == 1){
        printf("Que si!\n");
    }
    else{
        printf("Hmmm... :(\n");
    }

    char unaOpcion = 'c';
    switch(unaOpcion){
        case 'a':  
            printf("Escogieron la primera opción\n");
            break;

        case 'b':
            printf("Han escogido la opción del medio\n");
            break;

        case 'c':
            printf("Bueh, tocó la última\n");
            break;

        default:
            printf("No escogieron ningúna opción válida\n");
            break;
    }


    // Bucles:
    printf("Aquí va el contenido del array losNumerosDelUnoAlDiez:\n");
    int len = sizeof(losNumerosDelUnoAlDiez) / sizeof(float);
    for(int i=0; i<len; i++){
        printf("%i ", i);
    }
    printf("\n\n");

    printf("Cien repeticiones:\n");
    int j = 0;
    while(j < 100){
        printf("%i ",j);
        j++;
    }
    printf("\n\n");


    // Punteros:
    float *pUnNumero;
    float unNumero = 3.0;
    pUnNumero = &unNumero;
    printf("La dirección del puntero pUnNumero:%p\n", pUnNumero);
    printf("El dato que hay almacenado en pUnNumero:%f\n", *pUnNumero);

    printf("\n");

    return 0;
}

3. Otros Tipos Primitivos que no Existen en C:

Bool:

1
2
3
4
5
Boolean unBool = NO;
Boolean otroBool = YES;
if(unBool){
    printf("Seekas");
}

Existe ademas un tipo de variable 'id' que permite crear variables para almacenar cuaquier tipo de datos:

1
2
3
4
5
int unEntero = 2;
char unChar = 'B';

id weakTypedVar = unEntero; // Es un entero.
weakTypedVar = unChar; // Ahora es un caracter, no hay problema!

4. Clases y Objetos

Fraccion.h:

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

@interface Fraccion : NSObject {
    int numerador;
    int denominador;
}

-(void) setNumerador:(int)n;
-(void) setDenominador:(int)d;
-(int) numerador;
-(int) denominador;
-(void)setA:(int)n sobre:(int)d;

-(void) print;
-(double)convertToNumber;

@end

Fraccion.m:

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
#import "Fraccion.h"

@implementation Fraccion

-(void) setNumerador:(int)n{
    numerador = n;
}

-(void) setDenominador:(int)d{
    denominador = d;
}

-(int) numerador{
    return numerador;
}

-(int) denominador{
    return denominador;
}

-(void)setA:(int)n sobre:(int)d{
    numerador = n;
    denominador = d;
}

-(void) print{
    printf("%i/%i\n",numerador,denominador);
}

-(double)convertToNumber{
    if (denominador != 0) {
        return (double) numerador / denominador;
    }
    else{
        return 1.0;
    }
}

@end

main.m:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import <Foundation/Foundation.h>
#import "Fraccion.h"

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

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Fraccion* unaFraccion = [[Fraccion alloc] init];
    [unaFraccion setNumerador:3];
    [unaFraccion setDenominador:4];
    printf("Una fracción: ");
    [unaFraccion print];

    Fraccion* otraFraccion = [[Fraccion alloc] init];
    [otraFraccion setA:1 sobre:2];
    double num = [otraFraccion convertToNumber];
    printf("Otra fracción convertida a numero:%f\n", num);

    [pool drain];
    return 0;
}

5. Algunas Clases del Foundation Framework

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
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    // La función NSLog es equivalente a printf en programas de línea de comandos pero
    // en aplicaciones con interfaz gráfica para MacOS o iOS, es capaz de escribir a las
    // vitacoras del sistema.
    NSLog(@"Saludos!");

    // Strings:
    // http://developer.apple.com/library/mac/#documentation/cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html
    NSString* unString = @"Hola";
    NSLog(@"%@",unString);

    int i = 3;
    NSString* otroString = [NSString stringWithFormat:@"El entero ese de arriba vale %i", i];
    NSLog(@"%@",otroString);

    NSString* otroStringMas = [unString stringByAppendingString:otroString];
    NSLog(@"%@",otroStringMas);


    // NSNumber encapsula los tipos numericos primitivos de C en objetos de
    // Objective-C con el fin de que puedan interactuar con otros objetos del framework,
    // por ejemplo, ser agragados a NSArrays.
    // http://developer.apple.com/library/mac/#documentation/cocoa/Reference/Foundation/Classes/NSNumber_Class/Reference/Reference.html
    NSNumber* unNumeroEntero = [NSNumber numberWithInt:3];
    NSNumber* unNumeroDePuntoFlotante = [NSNumber numberWithFloat:3.141592];
    NSNumber* unBooleano = [NSNumber numberWithBool:YES];
    NSLog(@"El entero:%i, el flotante:%f y el booleano:%i", [unNumeroEntero intValue], [unNumeroDePuntoFlotante floatValue], [unBooleano boolValue]);

    // NSArray
    // http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
    NSArray* unArrayConStrings = [NSArray arrayWithObjects:@"Uno", @"Dos", @"Tres", nil];
    NSLog(@"%@", unArrayConStrings);

    NSArray* unArrayMezcladito = [NSArray arrayWithObjects:unNumeroDePuntoFlotante, otroString, nil];
    for(id objeto in unArrayMezcladito){
        NSLog(@"%@",objeto);
    }

    NSMutableArray* mutable = [NSMutableArray array];
    [mutable addObject:@"Uno"];
    [mutable addObject:[NSNumber numberWithInt:2]];
    [mutable addObject:[NSNumber numberWithFloat:3.14159]];
    [mutable removeObjectAtIndex:2];
    NSLog(@"%@",mutable);


    // NSDictionary
    // http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html

    NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
                          @"Valor 1", @"Clave 1",
                          [NSNumber numberWithFloat:2.0], @"Clave 2",
                          nil];
    id valor = [dict objectForKey:@"Clave 1"];
    NSLog(@"El valor: %@",valor);

    NSMutableDictionary* mutableDict = [NSMutableDictionary dictionary];
    [mutableDict setObject:@"Un Valor" forKey:@"clave1"];
    [mutableDict setObject:@"Otro Valor" forKey:@"clave1"];
    valor = [mutableDict valueForKey:@"clave1"];
    NSLog(@"El valor: %@",valor);

    [pool release];

    return 0;
}

6. Categorías

Las categorías son un mecanismo para extender una clase sin utilizar
herencia. Son muy útiles en casos como:

  1. Se quiere agregar nuevos métodos a una clase para la cual no se tiene
    el código fuente. Por ejemplo, se quiere agregar un método [NSString isEmpty].
    Sería poco práctico crear una clase que herede de NSString pues habría que
    cambiar todas las instancias de NSString en su programa por
    instancias de la nueva clase.

  2. Se quiere dividir la implementación de una clase muy grande en varios
    archivos o varios programadores.

  3. Se quiere arreglar un bug en la implementación de un método de una
    clase para la cual no se tiene el código.

Un Ejemplo:

NSString+IsEmpty.h

1
2
3
4
5
6
7
#import <Foundation/Foundation.h>

@interface NSString (NSString_IsEmpty)

-(BOOL)isEmpty;

@end

NSString+IsEmpty.m

1
2
3
4
5
6
7
8
9
10
11
#import "NSString+IsEmpty.h"

@implementation NSString (NSString_IsEmpty)

-(BOOL)isEmpty{
    NSString* trimmed = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

    return [trimmed length]==0;
}

@end

main.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <Foundation/Foundation.h>
#include "NSString+IsEmpty.h"

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

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSString* unString = @" ";
    if([unString isEmpty]){
        NSLog(@"The string is empty");
    }

    [pool drain];
    return 0;
}

7. Protocolos

Los protocolos son un mecanismo para asegurarse de que una clase
implemente ciertos métodos. Son muy útiles para implementar diseños
orientados a objetos desacoplados; en otras palabras, cuando se quiere
usar una clase de tipo id pero se debe asegurar que implemente cierto
método.

Un ejemplo: Muchas especies pueden cantar.

Cantante.h

1
2
3
@protocol Cantante <NSObject>
-(void)cante;
@end

Cerdo.h y Cerdo.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface Cerdo : NSObject {

}

-(void)grunha;

@end


@implementation Cerdo

-(void)grunha{
    NSLog(@"GGRRRHHÑFF");
}

@end

Pajaro.h y Pajaro.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface Pajaro : NSObject <Cantante> {

}

-(void)cante;

@end


@implementation Pajaro

-(void)cante{
    NSLog(@"Ti tiririri");
}

@end

main.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    //Cerdo* unCerdito = [[Cerdo alloc] init];
    //[unCerdito cante]; //El compilador emitirá una advertencia (Los cerdos no cantan) y el programa fallará en tiempo de ejecución en esta línea.

    // Aqui todo bien:
    Pajaro* unPajarito = [[Pajaro alloc] init];
    [unPajarito cante];

    // Pero la mejor manera de hacerlo (diseño desacoplado, el programa no depende de la existencia de la clase Pajaro, prodría llegar una Ballena y todo bien.):
    id<Cantante> unCantanteDeCualquierEspecie;
    unCantanteDeCualquierEspecie = [[Pajaro alloc] init];
    [unCantanteDeCualquierEspecie cante];


    [pool drain];
    return 0;
}

8. Manejo de Memoria

Todos los objetos tienen una propiedad retainCount. Cuando el
retainCount llega a cero, el objeto es destruido automáticamente y la memoria
es liberada. El conteo es manipulado con los métodos retain y release.

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
int main (int argc, const char * argv[]){
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // El retainCount de un objeto despues de creado es 1:
    NSNumber* unNumero = [[NSNumber alloc] initWithFloat:3.141592];
    NSLog(@"[unNumero retainCount]: %lu",[unNumero retainCount]);

    // Se puede manipular manualmente:
    [unNumero retain];
    NSLog(@"[unNumero retainCount]: %lu",[unNumero retainCount]);
    [unNumero release];
    NSLog(@"[unNumero retainCount]: %lu",[unNumero retainCount]);

    // Si se llama a release de nuevo, el objeto se destruirá y el programa
    // fallará en tiempo de ejecución si se le intenta usar de nuevo:
        //  [unNumero release];
        //  NSLog(@"[unNumero retainCount]: %lu",[unNumero retainCount]);
        //  NSLog(@"El valor de unNumero: %f",[unNumero floatValue]);


    // Cuando un objeto utiliza a otro, lo debe retener para evitar que se
    // destruya. Los NSArray, por ejemplo, retienen los objectos que son
    // agregados al array.
    NSMutableArray* unArray = [NSMutableArray array];
    [unArray addObject:unNumero];
     NSLog(@"[unNumero retainCount]: %lu",[unNumero retainCount]);

    // Ahora, si libero al numero, el array todavía lo estará reteniendo y
    // no se destruirá:
    [unNumero release];
    NSLog(@"[unNumero retainCount]: %lu",[unNumero retainCount]);
    NSLog(@"El valor de unNumero: %f",[unNumero floatValue]);


    [pool drain];
}

También existen los NSAutoreleasePool y el método [NSObject autorelease]
que sirven para aplazar la destrucción de objetos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
NSAutoreleasePool * aPool = [[NSAutoreleasePool alloc] init];

NSNumber* otroNumero = [[NSNumber alloc] initWithFloat:3.141592653];

// Haga algo con el número...

// Destruyalo luego
[otroNumero autorelease];

// Fijese que todavía existe:
NSLog(@"[otroNumero retainCount]: %lu",[otroNumero retainCount]);
NSLog(@"El valor de otroNumero: %f",[otroNumero floatValue]);

// Haga muchas mas cosas en su programa...

// Ahora si, libere memoria.
[aPool release];

// El numero ya no existe, las siguientes líneas matarían al programa.
    //  NSLog(@"[otroNumero retainCount]: %lu",[otroNumero retainCount]);
    //  NSLog(@"El valor de otroNumero: %f",[otroNumero floatValue]);

El Foundation framework establece una convencion que se deben tener
en cuenta: Todos los métodos que tienen las palabras "init", "alloc" o
"copy" crean un nuevo objeto y usted tiene la responsabilidad de
administrar la memoria para ese objeto (liberar y retener cuando sea
adecuado). Otros métodos que crean objetos como por ejemplo [NSArray
array]
que no tienen ninguna de esas palabras, entregan un objeto
"autoreleased" que se liberará automáticamente mas tarde.

Los objetos que usted cree también deben asegurarse de retener otros
objetos cuando los necesiten y liberarlos cuando ya no los necesite. Un ejemplo:

Cantante.h y Cantante.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@interface Cantante : NSObject {
    NSString* _nombre;
}

-(void)setNombre:(NSString*)n;
-(NSString*)nombre;

@end


@implementation Cantante

-(void)setNombre:(NSString*)n{
    _nombre = n;
}

-(NSString*)nombre{
    return _nombre;
}

@end

Banda.h y Banda.m

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
@interface Banda : NSObject {
    Cantante* _cantante;
}

-(Cantante*)cantante;
-(void)setCantante:(Cantante*)nuevoCantante;

-(void)despidaCantante;

@end


@implementation Banda

// Destructor:
- (void)dealloc{
    NSLog(@"Se acabó la banda...");
    [_cantante release];
    [super dealloc];
}

-(Cantante*)cantante{
    return _cantante;
}

-(void)setCantante:(Cantante*)nuevoCantante{
    if(nuevoCantante != _cantante){
        [self despidaCantante];
        _cantante = nuevoCantante;
        [_cantante retain];

        NSLog(@"%@ quedó contratado.", [_cantante nombre]);
    }
    else{
        NSLog(@"%@ ya estaba contratado!", [_cantante nombre]);
    }
}

-(void)despidaCantante{
    if(_cantante != nil){
        NSLog(@"%@ quedó despedido.", [_cantante nombre]);
        [_cantante release];
        _cantante = nil;       
    }
}

@end

main.m

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
int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Cantante* juancho = [[Cantante alloc] init];
    [juancho setNombre:@"Juancho"];
    NSLog(@"Retain count de Juancho antes de ser contratado: %lu", [juancho retainCount]);

    Banda* losDatsun = [[Banda alloc] init];
    [losDatsun setCantante:juancho];
    NSLog(@"Retain count de Juancho después de ser contratado: %lu", [juancho retainCount]);

    Cantante* pedro = [[Cantante alloc] init];
    [pedro setNombre:@"Pedro"];

    [losDatsun setCantante:pedro];
    NSLog(@"Retain count de Juancho:%lu y Pedro:%lu", [juancho retainCount], [pedro retainCount]);

    [losDatsun release];
    NSLog(@"Retain count de Pedro:%lu", [pedro retainCount]);

    [pool drain];
    return 0;
}

Afortunadamente Objective-C proporciona un mecanismo automático que
evita tener que escribir setters y getters manualmente:

9. Setters y Getters Sintetizados (@properties y @synthetize)

Aquí va el ejemplo anterior de nuevo, esta vez utilizando @properties y
@synthetize para generar los getters y setters. Esta es la manera
recomendada de crear sus clases

Cantante:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface Cantante : NSObject {
    NSString* _nombre;
}

@property(nonatomic,retain) NSString* nombre;

@end


@implementation Cantante

@synthesize nombre=_nombre;

@end

Banda

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
@interface Banda : NSObject {
    Cantante* _cantante;
}

@property(nonatomic, retain) Cantante* cantante;

-(void)despidaCantante;

@end

@implementation Banda

@synthesize cantante=_cantante;

// Destructor:
- (void)dealloc{
    NSLog(@"Se acabó la banda...");
    [_cantante release];
    [super dealloc];
}

-(void)despidaCantante{
    NSLog(@"%@ quedó despedido.", [_cantante nombre]);
    self.cantante= nil;
}

@end

main.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main (int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Cantante* juancho = [[Cantante alloc] init];
    juancho.nombre = @"Juancho";
    NSLog(@"Retain count de Juancho antes de ser contratado: %lu", [juancho retainCount]);

    Banda* losDatsun = [[Banda alloc] init];
    losDatsun.cantante = juancho;
    NSLog(@"Retain count de Juancho después de ser contratado: %lu", [juancho retainCount]);

    Cantante* pedro = [[Cantante alloc] init];
    [pedro setNombre:@"Pedro"];

    losDatsun.cantante = pedro;
    NSLog(@"Retain count de Juancho:%lu y Pedro:%lu", [juancho retainCount], [pedro retainCount]);

    [losDatsun release];
    NSLog(@"Retain count de Pedro:%lu", [pedro retainCount]);

    [pool drain];
    return 0;
}

10. Mas Recursos

iOs Developer Library: The Objective-C Programming Language
iOs Developer Library: Object Oriented Programming With Objective-C
iOs Developer Library: Foundation Framework Reference
iOs Developer Library: Memory Management Programming Guide
iOs Developer Library: Memory Management Programming Guide for Core Foundation

11. Ejercicio

Suponga que está escribiendo un juego de ciencia ficción con los siguientes requerimientos:

  • Existen 3 especies de personajes que los jugadores pueden escoger para
    jugar: Wookies, Humanos y Droids.
  • Existen 2 especies de enemigos que le harán la vida imposible a los
    jugadores: Storm Troopers y Siths.
  • Los jugadores y los enemigos se agrupan en equipos para pelear y
    apostar carreras de naves contra los otros.
  • Cuando un personaje es incluido en un equipo, saluda con el nombre al
    resto de sus compañeros.
  • Cuando un enemigo es incluido en un equipo, mira feo al resto de sus
    compañeros.
  • Los Droids no saben pelear.
  • Solo los Wookies y los Sith saben pilotear.
  • Cuando un equipo se enfrenta a otro, ganará el que tenga mas
    personajes o enemigos en capacidad de pelear. En caso de empate,
    ganará uno de los dos aleatoriamente. Pista: la expresión arc4random() % 74
    devuelve un entero aleatorio entre cero y 74.
  • El equipo que pierda una pelea, escogerá uno de sus integrantes
    aleatoriamente y este morirá.
  • Cuando un equipo apuesta una carrera de naves con otro, ganará quien
    tenga menos pasajeros. En caso de empate, ganará aleatoriamente uno de
    los dos. Todos los miembros del equipo perdedor mirarán feo a su
    piloto.

Haga lo siguiente:

  • Escriba las clases Personaje, Enemigo, Equipo, Wookie, Humano, Droid,
    StormTrooper y Sith. Con todos los métodos y propiedades que se
    necesiten.
  • Escriba los protocolos <Piloto> y <Soldado> e impleméntelos en las clases adecuadas.
  • No se deben presentar situaciones ilógicas como un equipo corriendo
    una carrera sin piloto, un personaje saludando al vacío, etc.
  • Cuando un personaje muere, se debe liberar el espacio que este ocupaba
    en memoria.

Se sabrá si se implementaron correctamente las clases si se ejecuta la
siguiente rutina y los mensajes impresos en la consola son los
esperados.

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
Wookie* chewbacca = [[Wookie alloc] initWithName:@"Chewbacca"];
Humano* hanSolo = [[Humano alloc] initWithName:@"Han Solo"];
Droid* c3PO = [[Droid alloc] initWithName:@"C3PO"];

Equipo* laAlianza = [[Equipo alloc] initWithName:@"La alianza"];
[laAlianza addIntegrante:chewbacca];
[laAlianza addIntegrante:hanSolo];
[laAlianza addIntegrante:c3PO];
[laAlianza assignPiloto:chewbacca];
[laAlianza assignSoldado:chewbacca];
[laAlianza assignSoldado:hanSolo];


StormTrooper* bobaFett = [[StormTrooper alloc] initWithName:@"Boba Fett"];
Sith* vader = [[Sith alloc] initWithName:@"Darth Vader"];

Equipo* elImperio = [[Equipo alloc] initWithName:@"El Imperio"];
[elImperio addIntegrante:bobaFett];
[elImperio addIntegrante:vader];
[elImperio assignPiloto:vader];
[elImperio assignSoldado:vader];
[elImperio assignSoldade:bobaFett];


[elImperio pelearContra:laAlianza];

[elImperio carreraContra:laAlianza];

El ejercicio a medias