Programación Con Objective-C
Programación Con Objective-C
Índice General
1 GNUstep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
3 El lenguaje Objective-C. . . . . . . . . . . . . . . . . . . . . . . 13
3.1 Palabras reservadas en Objective-C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Librerı́as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3 La función main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.4 Nuestro primer programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.5 Declaración de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.6 Las funciones printf() y scanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.7 Nuestro segundo programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.8 Operadores y sentencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.8.1 Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.8.2 Operadores relacionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.8.3 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.8.4 Sentencias condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.8.5 Sentencias iterativas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.9 Nuestro tercer programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.10 Nuestro cuarto programa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.11 Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.12 Nuestro quinto programa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.13 Otros operadores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.14 Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
8 Conceptos de dise~
no . . . . . . . . . . . . . . . . . . . . . . . . . 101
8.1 Delegate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
8.2 Cadena de eventos (Responder chain) . . . . . . . . . . . . . . . . . . . . . . . . 102
8.2.1 Eventos del ratón . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
8.2.2 Eventos del teclado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
8.3 Archivos Gorm. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
8.4 First Responder. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
8.5 Notificaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Índice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
Capı́tulo 1: GNUstep 1
1 GNUstep
http://www.gnustep.org/
http://gnustep.wordpress.com/
Capı́tulo 1: GNUstep 2
http://www.gnu.org/copyleft/lesser.html
2 Especificaciones OpenStep y
configuración
http://gnustep.wordpress.com/
Para poder seguir los ejemplos presentados en este libro, deben insta-
larse también las aplicaciones SystemPreferences y Gorm. Debe instalarse
también un editor de código, por ejemplo Emacs o Vim. O utilizarse uno más
sencillo como Gemas, el cual esta construido con GNUstep y tiene soporte
para archivos propios de este entorno.
Se necesitara también hacer uso de una Terminal o Shell. Nombres con
los que comúnmente se conoce al Interprete de comandos. Casi todos los
sistemas traen una Terminal instalada, que generalmente se encuentra en la
sección de Accesorios del menú de Aplicaciones. Para el caso de Windows, el
instalador se encarga de instalar este. Y se puede acceder a través del menú
del sistema en Programas → GNUstep → Shell o mediante la búsqueda de
aplicaciones con el nombre Shell. Para los usuarios que no tengan experi-
encia usando una Terminal, y especialmente para los usuarios de Windows,
recomendamos leer el apéndice A. El cual contiene lo básico para manejar
esta aplicación.
GNUstep ofrece una herramienta llamada ProjectCenter, la cual es un
entorno de desarrollo integrado o IDE (Integrated Development Environ-
ment), que facilita la creación de programas. Sin embargo, en este libro,
no se hará uso de dicha herramienta. Esto porque el IDE oculta parte del
procedimiento para crear un programa, y creemos que un principiante en
GNUstep debe tener una idea clara de dicho procedimiento. Sin embargo,
una vez entendido este, el uso de ProjectCenter no ofrecerá ningún misterio.
Capı́tulo 2: Especificaciones OpenStep y configuración 4
Imagen 0-1.
el menú una opción llamada Ocultar (Hide), la cual remueve del escritorio
todas las ventanas desplegadas por la aplicación, excepto el icono de esta.
Este estado se indica con un punto en la esquina inferior izquierda del icono.
Dando un doble clic sobre el icono todas las ventanas de la aplicación se
restauran a sus posiciones en el escritorio.
Imagen 0-2.
Imagen 0-3.
Imagen 0-4.
Imagen 0-5.
ventana. Mientras que las partes izquierda y derecha nos permiten modificar
la altura y el ancho simultáneamente.
Imagen 0-6.
Imagen 0-7.
Imagen 0-8.
Capı́tulo 2: Especificaciones OpenStep y configuración 9
El color que por defecto tienen las aplicaciones hechas con GNUstep, es
tan bien parte de las especificaciones OpenStep. Los tonos grises se eligieron
con el objeto de no cansar la vista de los usuarios.
Imagen 0-9.
El primer par de teclas First y Second Command son las teclas que por
defecto acceden a las opciones de los menús. Por ejemplo, en la imagen
se ve que la opción Quit del menú tiene asignada la combinación de teclas
#q, donde el carácter # representa la tecla Command. Para cerrar la apli-
cación, podemos entonces utilizar la combinación de teclas Command + q.
El segundo par de teclas First y Second Alternate, y el tercero First y Sec-
ond Control también pueden utilizarse para acceder al menú, aunque aquı́
nos serán útiles para realizar ciertas operaciones en la aplicación Gorm (que
veremos mas adelante). Cuando GNUstep se utiliza en Windows, todas las
teclas modificadoras se asignan a teclas como Control, Alt y Shift. Como se
puede comprobar en los menús, donde no se observa el carácter #.
Cuando no se utiliza el escritorio WindowMaker, los iconos de aplicación
y las miniventanas se irán acumulando uno sobre otro en la esquina inferior
izquierda del escritorio. Para evitar esto, se puede instalar la herramienta
IconManager, que permite ir colocando los iconos de una forma ordenada.
Capı́tulo 2: Especificaciones OpenStep y configuración 10
Imagen 0-10.
Imagen 0-11.
Debe darse clic en el botón Set después de establecer el valor deseado para
cada variable, de otra forma no se guardaran los valores en las preferencias.
La sección Date & Time de la aplicación SystemPreferences permite es-
tablecer la zona horaria. La sección Fonts permite establecer la fuente para
diferentes componentes gráficos de GNUstep. Y la sección Themes permite
establecer un tema diferente al establecido por defecto. El siguiente enlace
contiene algunos temas para GNUstep:
http://wiki.gnustep.org/index.php/Themes
3 El lenguaje Objective-C
Antes que nada, ¿Que es un programa? Podrı́amos decir que un programa
no es más que una serie de instrucciones que le indican a la computadora
las acciones que queremos que realice. Evidentemente no nos podemos co-
municar con la computadora como nos comunicamos con otra persona, para
hacerlo necesitamos de un lenguaje especial, es decir de un lenguaje de pro-
gramación. Como cualquier lenguaje humano, un lenguaje de programación
tiene un conjunto de palabras aceptadas (llamadas palabras reservadas) y
una sintaxis que nos dice como utilizar esas palabras.
Para comprender esto claramente, hagamos una analogı́a con el espa~ nol.
En el espa~ nol, las palabras reservadas serian las que son aceptadas por la
Real Academia de la Lengua, y la sintaxis es la forma correcta de utilizar esas
palabras. Por ejemplo, sabemos que es correcto decir “Vamos a la monta~ na”,
pero es incorrecto decir “Monta~ na a la vamos”. En ambos casos hicimos uso
de las mismas palabras, pero solo una de las frases es correcta. De la misma
forma, en un lenguaje de programación debemos conocer la forma correcta
de utilizar las palabras, de otra manera la computadora no comprenderá
nuestras instrucciones.
Ası́ como existen diferentes lenguajes humanos (espa~ nol, francés, inglés,
alemán, etc.), también existen diferentes lenguajes de programación.
GNUstep hace uso del lenguaje de programación Objetive-C. El cual
es necesario conocer antes de intentar programar (lo que equivaldrı́a a
poder hablar en Objective-C). Objective-C esta basado en el lenguaje
de programación C. Sólo que a diferencia de este, Objective-C ofrece la
posibilidad de utilizar el paradigma de programación orientada a objetos,
algo que veremos más adelante.
do if sizeof while
3.2 Librerı́as
Al momento de programar necesitaremos a veces de ciertas funciones como,
por ejemplo, la funciones seno, coseno, logaritmo, etc. Las cuales, por co-
modidad, ya se encuentran implementadas en las librerı́as. Una librerı́a no
es mas que un conjunto de funciones que podemos utilizar a la hora de pro-
gramar. Para entender esto, utilicemos una analogı́a con una calculadora
cientı́fica. Esta tiene varias teclas con distintas funciones, por ejemplo: raı́z
cuadrada, raı́z cúbica, logaritmo natural, seno, coseno, etc. Ası́, podemos de-
cir que las teclas de una calculadora cientı́fica constituyen un grupo de fun-
Capı́tulo 3: El lenguaje Objective-C 15
#import <math.h>
main (void)
{
Aquı́ van las instrucciones de nuestro programa.
}
#import <stdio.h>
main (void)
{
printf("Hola mundo \n");
}
cd practicas
./hola
Los datos presentados en la tabla no son los únicos que existen, pero son
los básicos para iniciarnos en la programación. La tabla también presenta
la capacidad de las variables int y float. Es importante tener presente,
cuando nuestro programa maneja números muy grandes o muy peque~ nos,
que las computadoras tienen sus limitaciones para expresar números. Es-
tas limitaciones dependen de la arquitectura de nuestra computadora, y los
valores listados en la tabla son sólo ciertos para determinadas arquitecturas.
Para declarar variables necesitamos darles identificadores, es decir nom-
bres. Sin embargo, estos identificadores deben cumplir con ciertas reglas, las
cuales son:
sumando1
_cantidad
masaCarro
Capı́tulo 3: El lenguaje Objective-C 19
1erCantidad
masa carro
%crecimiento
int numero;
float velocidad;
numero = 15;
velocidad = 8.25;
15 = numero;
Es recomendable que los nombres de las variables tengan relación con los
datos que contienen, como los ejemplos aquı́ mostrados. Podrı́amos llamar
a nuestras variables simplemente a, b, c, etc, pero estos nombres no nos
dirı́an nada acerca de los datos que contienen. Esto además nos facilita la
tarea de encontrar posibles errores o de hacerle modificaciones al código.
Más aun si revisamos el código semanas o meses después de haberlo escrito.
Lo recomendado en Objective-C, es que los nombres de las variables sean
combinaciones de palabras, donde la segunda, tercera, cuarta, etc, palabras,
comiencen con mayúscula. Por ejemplo, una variable que contenga la altura
de una persona podrı́a llamarse alturaPersona, donde la primera palabra
esta en minúsculas y la segunda comienza con mayúscula. Otro ejemplo seria
una variable que contenga el precio del manı́ japones, cuyo nombre podrı́a
ser precioManiJapones.
Por último debemos mencionar, aunque esto pueda parecer obvio, que
los nombres de las variables deben ser únicos. Es decir que no podemos
pretender utilizar el mismo nombre para distintas variables, ya que esto
crearı́a una confusión.
Por defecto, los datos de tipo float y double (números reales) se escriben
con 6 decimales. Sin embargo, podemos indicar la cantidad de decimales
deseados utilizando %.nf donde n es la cantidad de decimales que deseamos.
Por ejemplo, el siguiente código:
Produce la salida:
185463
185463
185463
185463
185463
Para las variables de tipo real, podemos especificar tanto la cantidad
de espacios a ocupar, como la cantidad de decimales. Esto se harı́a con
la combinación de caracteres %m.nf, donde m es la cantidad de espacios a
ocupar y n la cantidad de decimales. ¡Hágase la prueba!.
Pasemos ahora a considerar la función scanf(). Esta función lee los datos
ingresados a través del teclado y los almacena en las variables indicadas.
Debe tenerse presente que una entrada de datos desde el teclado se produce
cuando, después de escribir el dato, el usuario presiona la tecla ENTER.
Mientras el usuario no presione esta tecla, no habrá un ingreso de datos. La
sintaxis de esta función es:
La cadena de texto entre comillas lista los formatos de los datos que se
van a ingresar, los que se almacenaran en las variables indicadas. Esto es, el
primer dato se almacena en variable1, el segundo dato en variable2, etc.
Obsérvese el carácter & delante del identificador de cada variable. Como en
el caso de la función anterior, los puntos suspensivos indican que se pueden
leer cuantos datos se deseen. Por ejemplo, el siguiente código, le indica al
usuario que ingrese su número de carné y su edad:
Obsérvese que como los dos datos esperados son de tipo int, los tipos de
datos se han declarado como %d%d, un %d para cada dato. Noté además que
no es necesario dejar espacios entre estos caracteres. Y puesto que los datos
solamente son ingresados hasta que se presiona la tecla ENTER, solamente
se puede ingresar un dato por lı́nea y no es necesario usar los caracteres \n.
Capı́tulo 3: El lenguaje Objective-C 23
#include <stdio.h>
main(void)
{
float numero1, numero2, suma, resta, mult, div;
m = 21%7;
n = 22%7;
p = 24%7;
r = 5 + 6*8;
Operador Significado
== Igual a.
!= No es igual a.
m < n
p == q
r <= s
t != u
|| O (Or). (condición1) ||
(condición2) || ...
|| (condiciónN)
Capı́tulo 3: El lenguaje Objective-C 26
Sera verdadera en tres casos: cuando las tres condiciones simples sean
verdaderas, cuando dos de estas sean verdaderas o cuando sólo una de estas
sea verdadera. Y sera falsa sólo en el caso de que las tres condiciones simples
sean falsas.
Por último, una condición que hace uso del operador ! como, por ejemplo:
!(a == 31)
if (condición)
{
Acciones a realizar si
la condición se cumple
}
else
{
Acciones a realizar si
la condición no se cumple
}
if (condición)
{
Acciones a realizar si
la condición se cumple
}
{
printf("Usted es menor de edad");
}
switch (variable)
{
case valor1:
Instrucciones a realizar si
variable es igual a valor1.
break;
case valor2:
Instrucciones a realizar si
variable es igual a valor2.
break;
case valor3:
Instrucciones a realizar si
variable es igual a valor3.
break;
...
...
Capı́tulo 3: El lenguaje Objective-C 29
case valorN:
Instrucciones a realizar si
variable es igual a valorN.
break;
default:
Instrucciones a realizar si
variable no es igual a ninguno
de los valores anteriores.
}
Donde se hace uso de las palabras reservadas switch, case, break y default.
Los puntos suspensivos indican que se pueden agregar cuantos valores se de-
seen para variable. El funcionamiento de esta sentencia es fácil de entender.
Cuando variable es igual a valor1, se ejecutan las instrucciones correspon-
dientes a este caso. Y lo mismo para cuando variable es igual a cualquier
otro valor. Solamente cuando variable no es igual a ninguno de los val-
ores indicados, es cuando se ejecutan las instrucciones de la opción default.
Observe que la opción default no necesita la palabra reservada break. En
realidad la opción default es opcional. Sin embargo, es recomendable que el
programa tenga instrucciones que llevar a cabo, para cuando la variable no
tenga ninguno de los valores esperados. El dato a evaluar, variable, debe ser
un dato numérico.
En caso de que queramos que para un rango determinado de números se
ejecuten las mismas instrucciones, podemos utilizar una forma modificada
de la opción case, la cual es:
Obsérvense los tres puntos entre valorA y valorB, los cuales deben estar
separados por un espacio de ambos valores. Un ejemplo de este caso serı́a:
case 11:
case 49:
Capı́tulo 3: El lenguaje Objective-C 30
case 93:
printf("El valor es 11, 49 ó 93");
break;
int x, mult;
3 por 1 = 3
3 por 2 = 6
3 por 3 = 9
3 por 4 = 12
3 por 5 = 15
3 por 6 = 18
3 por 7 = 21
3 por 8 = 24
3 por 9 = 27
3 por 10 = 30
3 por 11 = 33
Capı́tulo 3: El lenguaje Objective-C 31
3 por 12 = 36
while (condición)
{
Instrucciones a repetir
}
int x = 1, mult;
do
{
Instrucciones a repetir
Capı́tulo 3: El lenguaje Objective-C 32
}
while (condición)
#import <stdio.h>
main(void)
{
int numero1, numero2, numero3, mayor, intermedio, menor;
}
}
else
{
mayor = numero2;
intermedio = numero1;
#import <stdio.h>
main(void)
{
int eleccion = 0;
float numero1, numero2, resultado;
{
printf("\n");
printf("Elija una opcion para llevar a cabo: \n");
printf("1. Sumar dos numeros. \n");
printf("2. Restar dos numeros. \n");
printf("3. Multiplicar dos numeros. \n");
printf("4. Dividir dos numeros. \n");
printf("5. Salir. \n");
printf("\n");
scanf("%d", &eleccion);
switch (eleccion)
{
case 1:
printf("Ingrese los dos numeros: \n");
scanf("%f%f", &numero1, &numero2);
resultado = numero1 + numero2;
break;
case 2:
printf("Ingrese los dos numeros: \n");
scanf("%f%f", &numero1, &numero2);
resultado = numero1 - numero2;
break;
case 3:
printf("Ingrese los dos numeros: \n");
scanf("%f%f", &numero1, &numero2);
resultado = numero1 * numero2;
break;
case 4:
printf("Ingrese los dos numeros: \n");
scanf("%f%f", &numero1, &numero2);
resultado = numero1 / numero2;
break;
default:
eleccion = 5;
}
if (eleccion != 5)
{
printf("El resultado es %.2f. \n", resultado);
}
}
}
3.11 Funciones
Las funciones son un conjunto de instrucciones que realizan una tarea deter-
minada, como las funciones de las librerı́as. Y que podemos llamar cuando
queramos que dicha tarea se lleve a cabo. Las funciones nos permiten dividir
un programa en peque~ nos módulos, que luego llamamos para que ejecuten
sus tareas especificas. Esto hace mas fácil el dise~
no de un programa. Las fun-
ciones pueden recibir datos y devolver un dato como resultado de su tarea.
Aunque, por supuesto, es posible que una función no reciba ningún dato, y
que no devuelva ninguno. En general, la sintaxis de una función es de la
siguiente forma:
Donde tipo devuelto es el tipo del dato que la función devolverá o re-
tornara. nombre es el nombre de la función y parámetros son los datos
que la función necesita para su funcionamiento. Si la función no devolverá
ningún dato, entonces no se especifica ningún tipo de dato. Sin embargo, si
la función no necesita de ningún parámetro, los paréntesis deben colocarse
encerrando la palabra void.
Un ejemplo de función que no necesita de ningún parámetro, y que no de-
vuelve o retorna ningún dato, es una que simplemente imprime un mensaje,
por ejemplo:
imprimirSaludo (void)
{
printf("Bienvenido al programa.");
}
imprimirSaludo;
Una función que no retorna ningún dato, pero que requiere un parámetro,
necesita que el parámetro se le pase entre paréntesis (en el caso de varios
parámetros, estos se separan con comas). Por ejemplo, la función may-
orDeEdad vista anteriormente, puede usarse de la siguiente forma:
mayorDeEdad (edadCliente);
donde la variable edadCliente debe ser de tipo int, puesto que este fue
el tipo de parámetro declarado en la función. Obsérvese que el nombre de
la variable que se pasa como parámetro, no necesita ser el mismo que el
nombre del parámetro declarado en la función (en este caso edad ). En lugar
de pasar una variable como parámetro, también puede pasarse directamente
un valor. Por ejemplo:
mayorDeEdad (56);
exito = imprimirOtroSaludo;
imprimirOtroSaludo;
que estos parámetros, base y altura, no necesitan tener los mismos nombres
que las variables declaradas como parámetros en la función (lado1 y lado2 ).
Hasta aquı́, hemos visto como crear y utilizar funciones. Sin embargo,
para que nuestras funciones puedan ser utilizas, primero deben declararse
al inicio del programa. Esto es muy sencillo, ya que para para hacerlo sim-
plemente debemos indicar el tipo de dato devuelto, el nombre de la función
y los parámetros que esta recibe. Básicamente, esto es copiar la primera
linea de una función, y agregar un punto y coma al final. Por ejemplo, para
declarar las cuatro funciones que hemos ejemplificado aquı́, tendrı́amos que
escribir el siguiente código:
imprimirSaludo (void);
mayorDeEdad (int edad);
int imprimirOtroSaludo (void);
float calculoArea (float lado1, float lado2);
#import <stdio.h>
main(void)
{
float base, altura, resultado;
x += y; equivale a x = x + y;
x -= y; equivale a x = x - y;
x *= y; equivale a x = x * y;
x /= y; equivale a x = x / y;
x = 5;
y = 3 * (++x);
Capı́tulo 3: El lenguaje Objective-C 40
x = 5;
y = 3 * (x++);
3.14 Comentarios
Es recomendable, cuando un programa llega a cierto grado de complejidad,
introducir comentarios en el código de este. De forma que cuando lo revise-
mos semanas, meses o incluso a~ nos después, comprendamos rápidamente el
funcionamiento del código. En Objective-C existen dos formas de agregar
comentarios en un programa, y estos se pueden agregar en cualquier parte
de este. La primera de ellas, utiliza los caracteres // para indicar que lo
sigue hasta el final de la linea es un comentario. Por ejemplo:
Los objetos en programación, al igual que los objetos reales, pueden tener
diferentes estados. Por ejemplo, un interruptor tiene dos estados, encendido
y apagado. Los datos de un objeto, los cuales sólo se pueden acceder a través
de sus métodos, son quienes determinan el estado en el cual se encuentra un
objeto. Y dependiendo del estado en que se encuentre el objeto, ası́ sera su
comportamiento.
El concepto de objeto, se asemeja mas a la forma en que hacemos las
cosas en el mundo real. Para construir un carro, por ejemplo, se ensambla
una gran cantidad de objetos: motor, transmisión, carcasa, caja de cambios,
etc. Cada uno de los cuales realiza un conjunto de tareas especificas. De
esta forma, podemos visualizar un programa como un conjunto de objetos
que interactúan entre si.
que es lo que nosotros vemos de él: su color, pantalla, teclas, etc. Y una
implementación, que consiste en sus circuitos internos que implementan las
funciones del teléfono. La interfaz de un objeto es, entonces, aquello que el
resto del programa puede ver y con lo cual puede interactuar, mientras que
su implementación es la parte interna del objeto que lo hace funcionar. La
cual no esta disponible para el resto del programa.
Básicamente, podemos clasificar a los objetos en visuales y no visuales.
Los objetos visuales de un programa, son aquellos que el usuario puede ver y
con los cuales puede interactuar. Estamos muy familiarizados con los objetos
visuales de un programa: ventanas, botones, casillas de selección, barras de
desplazamiento, menús, listas desplegables, etc. Sin embargo, los objetos no
visuales, aquellos que no tienen una representación visual, también juegan un
papel importante en el funcionamiento de un programa. La siguiente imagen
presenta un sencillo programa hecho en GNUstep. Como se puede apreciar,
este esta compuesto por varios objetos visuales. Un objeto ventana, con el
titulo ‘Aplicación de Suma’. Dos objetos etiqueta, con los textos ‘+’ y ‘=’.
Tres objetos caja de texto, dos para los sumandos y uno para el resultado
de la suma. Y un objeto botón, con el titulo ‘Sumar’, para llevar a cabo la
suma. En conjunto, los objetos visuales también se conocen como controles.
Al inicio de esta sección, discutı́amos que cada uno de los objetos que nos
rodean, pertenecen a una cierta clase. Esto se cumple también en la progra-
mación orientada a objetos, donde cada objeto pertenece a una determinada
clase. Básicamente, podemos decir que una clase, es la definición de un ob-
jeto. Ası́ como ‘Medio de transporte de cuatro ruedas’, es la definición
de los objetos de la clase carro. Sin embargo, en programación, una clase es
la definición de un objeto, en el sentido de que constituye el código que tienen
en común todos los objetos de esa clase. Por ejemplo, todas las ventanas
tienen una barra de titulo, bordes, un botón para cerrar, etc. Pero no solo
esto, la clase también define el comportamiento de los objetos. Por ejemplo,
el comportamiento de una ventana al mover sus bordes para modificar su
dimensión.
Capı́tulo 4: Programación orientada a objetos 44
Podemos decir entonces que una clase, es el plano para construir un objeto
con sus aspectos y comportamiento básicos. Pero ¿cual es la razón de que
hayan clases?. Pues antes que nada, facilitarnos las cosas a la hora de dise~
nar
un programa. Si, por ejemplo, nuestro programa deberá tener una ventana
con su barra de titulo y un botón para cerrarla, entonces podemos crear esta
a partir de la clase ventana. Es decir, a partir del plano de construcción de
una ventana. Para luego, de ser necesario, modificarla y adaptarla a nuestros
propósitos. Esto, evidentemente, es mucho más sencillo que construir nuestra
ventana desde cero.
la jerarquı́a, por esto se le llama clase raı́z (root class). Observe, también,
que hay clases que no tienen subclases, como la clase NSBox.
Cuando se dice que una clase deriva de otra, lo que se quiere decir es que
la clase adopta todos los métodos de la clase de la cual deriva. Por ejemplo, la
clase NSResponder, al derivar de la clase NSObject, tiene todos los métodos
de esta. Pero además, incorpora los métodos propios de su clase. Lo mismo
sucede con las clases NSMenu y NSImage, que adoptan todos los métodos
de la clase NSObject. Pero, a su vez, incorporan métodos propios de sus
clases. Esto significa que las clases NSResponder, NSMenu y NSImage, tienen
en común los métodos de la clase NSObject. A esto se le llama herencia,
puesto que una clase hereda los métodos de la clase de la cual deriva. Es
decir, de su superclase. Una clase en particular puede tener un método con
el mismo nombre que uno presente en su superclase, o de otra clase más
arriba en la jerarquı́a. A esto se le conoce como redefinir un método. Y más
adelante veremos lo útil que puede ser en algunas situaciones.
El lector podrá preguntarse el porque de la herencia. Bueno, la herencia se
da debido a que las clases se crean a partir de clases más básicas. Esto se hace
ası́, porque es más fácil crear una clase a partir de otra, que crearla a partir
de cero (bueno, en realidad hay otras razones para hacer esto, pero no las
discutiremos aquı́). Lo importante de conocer el concepto de herencia, es que
en el sistema de documentación de GNUstep, solo aparecen documentados
los métodos propios de cada clase, y no los que heredan. Sin embargo, es
importante saber que un objeto creado a partir de una cierta clase, puede
responder no sólo a los métodos de su clase, sino también a los métodos de
su superclase. Y a los de la superclase de su superclase, y ası́ sucesivamente
hasta llegar a la clase raı́z. Por ejemplo, un objeto creado a partir de la clase
NSBox (figura 3-3), responde no sólo a sus propios métodos, sino también a
los métodos de las clases NSView, NSResponder y NSObject. Esto puede
parecer muy teórico, pero es necesario saberlo.
Ası́ como para manejar números enteros o reales, debı́amos declarar vari-
ables de tipo int o float, para manejar objetos debemos declarar variables
de tipo objeto. Conocidas como punteros a objetos o simplemente objetos.
Estas se declaran como las variables, con la diferencia de que el identificador
de nuestro objeto, debe ir precedido por el carácter *. Por ejemplo, para
declarar un objeto llamado cuadro, cuya clase sea NSBox, tendrı́amos:
NSBox *cuadro;
id nombre_objeto;
Capı́tulo 4: Programación orientada a objetos 46
@end
Los métodos de instancia van precedidos por un signo menos (-), y los
métodos de clase por un signo más (+). La segunda diferencia, es que el
tipo del dato retornado por el método, debe ir indicado entre paréntesis.
Y, en caso de que no se retorne ningún dato, la palabra void debe ir entre
los paréntesis. La estructura básica de un método de instancia es (para un
método de clase lo único que cambia es el signo):
#import <AppKit/AppKit.h>
- (void) display;
Capı́tulo 4: Programación orientada a objetos 48
@implementation nombre_de_la_clase
@end
#import "Control.h"
@implementation Control
@end
4.4 Mensajes
La forma en que los objetos pueden comunicarse entre si, es mediante men-
sajes. La sintaxis de un mensaje que no requiere parámetros es:
[nombre_objeto nombre_método];
Capı́tulo 4: Programación orientada a objetos 49
id outletOne;
Una vez realizada la conexión de este outlet, podemos utilizar esta vari-
able para mandarle mensajes al objeto al cual este conectado. La conexiones
entre outlets y objetos, se establecen gráficamente mediante la aplicación
Gorm, lo cual veremos en el próximo capı́tulo. Es recomendable que los
nombres de los outlets, hagan referencia al objeto al cual están conectados.
Con las conexiones mostradas en la figura 3-5, el objeto a la izquierda puede
enviar mensajes a los otros dos objetos. El objeto a la derecha puede en-
viar mensajes únicamente al objeto de la izquierda, y el objeto en la parte
superior no puede enviar mensajes a ninguno de los otros objetos.
#import <AppKit/AppKit.h>
int
main(int argc, const char *argv[])
Capı́tulo 5: Nuestra primera app con GNUstep 54
{
return NSApplicationMain (argc, argv);
}
#import <Foundation/Foundation.h>
//Action
- (void) makeSum: (id)sender;
@end
Los primeros dos outlets, estarán conectados a las cajas de texto donde
el usuario ingresara los números a sumar. Mientras que el tercero, estará
conectado a la caja de texto utilizada para desplegar el resultado de la suma,
la cual sera llevada a cabo por el action -makeSum:. Ahora para el archivo
de implementación, tenemos el siguiente código:
#import <AppKit/AppKit.h>
Capı́tulo 5: Nuestra primera app con GNUstep 55
#import "SumaController.h"
@implementation SumaController
a = [firstNumber floatValue];
b = [secondNumber floatValue];
c = a + b;
@end
Dentro del método, primero se declaran tres variables de tipo float. Las
primeras dos se utilizan para guardar los números ingresados por el usuario.
Los cuales se obtienen usando el método -floatValue, el cual devuelve
el valor numérico, en tipo float, del contenido de la caja de texto. La
suma de estos números se almacena entonces en la tercera variable. Y por
último, se utiliza el método -setFloatValue:, para mostrar el resultado en
la correspondiente caja de texto. Por claridad, hemos llevado ha cabo la
suma de esta forma. Sin embargo, es posible reducir todo esto a la siguiente
forma:
El código dentro del método puede también escribirse en una sola linea.
Estos son los tres archivos de código que necesita nuestra app. En la
siguiente sección, crearemos la interfaz gráfica y posteriormente el archivo
de construcción.
ya, y arrastremos tres cajas de texto, dos etiquetas (de las que tienen el texto
System) y un botón. Los controles pueden reubicarse arrastrándolos con el
ratón y su tama~ no se puede modificar arrastrando con el ratón los nodos
de color rojo. El texto que muestran puede editarse dando doble clic sobre
ellos. Editemos entonces el texto de las etiquetas, en una de ellas escribamos
+ y en la otra =. Borremos el contenido de las cajas de texto y escribamos
como tı́tulo del botón Sumar. Redimensionemos la ventana y los controles, y
reubiquemos estos de tal forma que el resultado final se vea como la imagen
4-2.
Hasta aquı́ es suficiente para que nuestra interfaz gráfica sea funcional.
Sin embargo, vamos a modificarle algunos otros aspectos para mejorarla.
Seleccionemos la ventana de nuestra interfaz, ya sea dando un clic en alguna
parte de la ventana que no contenga algún control o seleccionando el icono
My Window en la ventana del documento. Ahora en la lista desplegable
en la parte superior del Inspector, seleccionemos la opción Attributes. Aquı́
podemos cambiar el tı́tulo de nuestra ventana a algo mas descriptivo. Por
ejemplo: App para sumar. Quitemos también la marca de la opción Resize
bar, para que el tama~no de nuestra ventana no se pueda modificar. Seguida-
mente seleccionemos la caja de texto de la derecha, donde se mostrara el
resultado. Y, en el Inspector, quitemos la marca de la opción Editable, para
que el usuario no pueda editar el contenido de esta caja de texto.
Seguidamente estableceremos la forma en que el usuario, mediante el
uso de la tecla TAB, puede moverse a través de los controles de nuestra
interfaz. Queremos que el usuario pueda ir de la primera caja de texto (la
de la izquierda) a la segunda caja de texto (en medio) y luego al botón
Sumar. Para posteriormente poder retornar a la primera caja de texto.
Hagamos entonces una conexión desde la primera caja de texto a la segunda,
seleccionando en el Inspector el outlet nextKeyView y dando clic en el botón
Connect. De forma similar realicemos la conexión de la segunda caja al
botón Sumar, y de este a la primera caja.
Finalmente vamos a decirle a la ventana, que la primera caja de texto
sea la que tenga el enfoque en cuanto la ventana de nuestra app este ac-
tiva. Esto es razonable, ya que es allı́ donde debe ingresarse el primer dato.
Para ello, hagamos una conexión desde el icono de nuestra ventana (el que
aparece en la ventana de nuestro documento) hasta la primera caja de texto,
seleccionando en el Inspector el outlet initialFirstResponder y dando clic en
el botón Connect.
Con esto hemos terminado nuestra interfaz gráfica. Ası́ que guardemos
los cambios hechos a nuestro documento y cerremos Gorm. Gorm guarda
la interfaz gráfica en una carpeta con extensión ‘gorm’, la cual contiene tres
archivos para recrear posteriormente la interfaz.
Capı́tulo 5: Nuestra primera app con GNUstep 61
include $(GNUSTEP_MAKEFILES)/common.make
# Nombre de la app
APP_NAME = Suma
# Recursos de la app
Suma_RESOURCE_FILES = suma.gorm
# Otros archivos
Suma_OBJC_FILES += SumaMain.m
# Makefiles
include $(GNUSTEP_MAKEFILES)/application.make
aplicación. Una app puede tener varios archivos de interfaz gráfica, pero sólo
uno de ellos puede ser el principal. En la siguiente entrada, Suma_RESOURCE_
FILES, se indican los recursos de nuestra app. Los recursos de una app,
son los archivos que no contienen código. Por ejemplo, imágenes, sonidos,
documentos, archivos de interfaz gráfica, etc. Es este caso, nuestra app sólo
tiene un recurso, el archivo de interfaz gráfica. En las siguientes dos entradas
se listan, respectivamente, los archivos de interfaz e implementación de las
clases presentes en nuestro proyecto. En nuestro caso, se trata de los archivos
‘SumaController.h’ y ‘SumaController.m’, respectivamente. Finalmente,
en la última entrada, se listan otros archivos de código que estén presentes
en nuestro proyecto. En este caso, solamente queda el archivo ‘SumaMain.m’.
Y la última linea, incluye un archivo que depende del tipo de proyecto que
queramos construir. En este caso queremos construir una aplicación, por
lo que se incluye el archivo ‘application.make’. Guardemos este archivo
con el nombre ‘GNUmakefile’ en la carpeta de nuestro proyecto. Y con esto
estamos listos para compilar y probar nuestra primera app.
german@german-desktop:$ make
Making all for app Suma...
Creating Suma.app/....
Compiling file SumaController.m ...
Compiling file SumaMain.m ...
Linking app Suma ...
Creating Suma.app/Resources...
Creating stamp file...
Creating Suma.app/Resources/Info-gnustep.plist...
Creating Suma.app/Resources/Suma.desktop...
Copying resources into the app wrapper...
german@german-desktop:$
openapp ./Suma.app
./Suma.app/Suma
Y con esto hemos terminado nuestra primera app hecha con GNUstep.
Para mayor información sobre el uso de la herramienta gnustep-make,
consúltese el apéndice B. Donde se abordan, entre otros temas, el proced-
imiento de instalación de un programa.
Capı́tulo 6: Clases y objetos en Objective-C 64
6.3 Métodos
Ya hemos comentado en un capı́tulo anterior como se implementan los
métodos. Y ya hemos escrito también un sencillo método. Sin embargo,
para que este tema quede claro, veamos aquı́ unos cuantos ejemplos mas.
Comencemos con un método que no recibe ningún parámetro y que no de-
vuelve ningún dato. Este seria de la siguiente forma:
- (void) printName
{
printf("Carlos.");
}
- (id) dataForController
{
return data;
}
Obsérvese que en este caso la palabra id, entre paréntesis, indica que se
devolverá un objeto cuya clase se desconoce. Noté también que el objeto es
devuelto con return, de forma similar a lo que ocurre en las funciones.
Capı́tulo 6: Clases y objetos en Objective-C 66
return box;
}
return w;
}
Donde x y y, son dos variables de tipo int, con los datos respectivos del
ancho y el alto de la ventana a crear.
@end
@end
- (BOOL) removeContent;
- (void) setContent: (id)content;
@end
@end
Implementacion.
@end
Implementacion.
@end
@implementation GemasEditorView
...
...
Un array es una lista de objetos. En este caso, hemos creado una lista
que contiene las cadenas de texto Uno, Dos, Tres y Cuatro (nil solamente
indica el final de la lista y no se incluye en esta). Este array creado de esta
forma es autoliberado. Y no debemos preocuparnos por la administración
de la memoria que este utiliza.
[lista release];
RELEASE(lista);
6.7 Ejemplos
En esta sección vamos a crear diferentes Tools para ejemplificar lo visto
anteriormente. Como ya hemos mencionado, una Tool es un programa de
lı́nea de comandos.
#import <Foundation/Foundation.h>
int
main(int argc, const char *argv[])
{
NSLog(@"Hola mundo!");
Capı́tulo 6: Clases y objetos en Objective-C 72
return 0;
}
include $(GNUSTEP_MAKEFILES)/common.make
# Otros archivos
Hola_OBJC_FILES += HolaMain.m
# Makefiles
include $(GNUSTEP_MAKEFILES)/tool.make
./obj/Hola
2013-05-21 23:30:58.524 Hola[2665] Hola mundo!
#import <stdio.h>
#import <Foundation/Foundation.h>
int
main(int argc, const char *argv[])
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
[pool release];
return 0;
}
include $(GNUSTEP_MAKEFILES)/common.make
TOOL_NAME = Era
Era_OBJC_FILES += EraMain.m
Capı́tulo 6: Clases y objetos en Objective-C 74
include $(GNUSTEP_MAKEFILES)/tool.make
./obj/Era
Han transcurrido 735010 dias
#import <stdio.h>
#import <Foundation/Foundation.h>
@implementation MyClass
+ (void) classString
{
printf("Texto de la clase MyClass. \n");
}
- (void) sayHello
{
printf("Hola Mundo. \n");
Capı́tulo 6: Clases y objetos en Objective-C 75
int
main(int argc, const char *argv[])
{
id pool = [[NSAutoreleasePool alloc] init];
[MyClass classString];
[objeto release];
[pool release];
return 0;
}
include $(GNUSTEP_MAKEFILES)/common.make
Capı́tulo 6: Clases y objetos en Objective-C 76
TOOL_NAME = Clase
Clase_OBJC_FILES += ClaseMain.m
include $(GNUSTEP_MAKEFILES)/tool.make
./obj/Clase
Texto de la clase MyClass.
Hola Mundo.
Hola GNUstep!.
- (id) init
{
self = [super init];
return self;
}
if (self)
{
// Aqui van nuestras modificaciones.
}
return self;
}
Capı́tulo 6: Clases y objetos en Objective-C 80
// Modificaciones.
return self;
}
- (void) dealloc
{
[date release];
[super dealloc];
}
NSArray *lista;
lista = [NSArray arrayWithObjects: @"A", @"B", nil];
[lista retain];
#import <stdio.h>
#import <Foundation/Foundation.h>
// Metodos Getter
- (NSUInteger) age;
- (NSString *) name;
// Inicio
- (id) init;
// Impresion de variables
- (void) printVars;
Capı́tulo 6: Clases y objetos en Objective-C 83
@end
@implementation Say
- (id)init
{
self = [super init];
if (self)
{
age = 0;
name = @"Ninguno";
//name = nil; //Establece ’name’ a nada.
}
return self;
}
// Metodos Setter
- (void) setAge: (NSUInteger)aNumber
{
age = aNumber;
}
/* Es incorrecto hacer:
*/
}
// Metodos Getter
- (NSUInteger) age
{
return age;
}
- (NSString *) name
{
return name;
}
Say *speaker;
NSString *boy = @"Pancho";
NSString *girl = @"Ana";
[speaker printVars];
[speaker setAge: 25];
[speaker setName: boy];
[speaker printVars];
[speaker setAge: 28];
[speaker setName: girl];
[speaker printVars];
[speaker release];
[pool release];
return 0;
}
Capı́tulo 6: Clases y objetos en Objective-C 85
if (name != nil)
{
[name release];
}
if (aName != nil)
{
name = [aName retain];
}
else
{
name = nil;
}
include $(GNUSTEP_MAKEFILES)/common.make
TOOL_NAME = Say
Say_OBJC_FILES += Say.m
include $(GNUSTEP_MAKEFILES)/tool.make
Modifı́quese el programa para que el valor inicial de name sea nil (hay
que compilar nuevamente el proyecto). Y ejecútese la herramienta para ver
la diferencia.
nil se utiliza para indicar que un puntero no tiene asignado objeto alguno.
En otras palabras, se utiliza para indicar que el puntero esta vacı́o.
6.10 Subclases
Ya que una subclase responde a todos los métodos de su superclase, es de
esperarse que una instancia de esta pueda comportarse (y ser tratada) como
una instancia de la superclase. Para entender esto veamos un ejemplo. Todos
los objetos de clase NSView (o subclases de esta) responden al método -
window, el cual retorna la ventana donde se encuentre el view en cuestión.
De acuerdo a la documentación, el objeto retornado es de la clase NSWindow.
De esta forma, el siguiente código obtiene el tı́tulo de dicha ventana:
#import <AppKit/AppKit.h>
//Setter
- (void) setView: (NSView *)aView;
- (void) setColor: (NSColor *)aColor;
//Getter
- (NSView *) view;
- (NSColor *) color;
@end
#import <Foundation/NSObject.h>
//Setter
- (void) setView: (NSView *)aView;
- (void) setColor: (NSColor *)aColor;
//Getter
- (NSView *) view;
- (NSColor *) color;
@end
@class NSColor;
@class NSView;
Lectura Recomendada: El peque~ no documento Clases Básicas de la li-
brerı́a GNUstep Base presenta y ejemplifica el uso de las clases NSString,
NSArray y NSDictionary. También trata el tema de los archivos Property
List, de los que haremos uso mas adelante. Este puede obtenerse en el enlace
http://gnustep.wordpress.com/, en la sección Documentos y Manuales.
Capı́tulo 7: Otra app de ejemplo 89
7.1 Un cronómetro
Construyamos primero la interfaz gráfica del cronómetro. Abramos Gorm y
seleccionemos en el menú Document → New Application. Ahora agregue-
mos una caja de texto a la ventana, estableciendo en sus propiedades una
alineación centrada, que no sea editable y que no sea seleccionable. Asumire-
mos que la interfaz gráfica se guarda con el nombre Chronometer. Este
cronómetro debe comenzar su funcionamiento al momento de ser lanzada la
aplicación y en la caja de texto deberá mostrar el avance de los segundos.
Un posible modelo para esta aplicación se presenta en la figura 7-1.
Ası́, necesitamos un objeto que controle el avance del tiempo y que comu-
nique este avance a la caja de texto. Crearemos entonces una clase a la que
llamaremos Chronometer, y que sera una subclase de la clase raı́z NSObject.
Esta clase tendrá un outlet para comunicarse con la caja de texto y una vari-
able de instancia para llevar el conteo de los segundos, ası́ como un método
encargado de llevar a cabo dicho conteo. Por lo tanto, la interfaz de esta
clase, a la que llamaremos ‘Chronometer.h’, es la siguiente:
#import <AppKit/AppKit.h>
//Outlets
id textBox;
}
- (void) seconds: (NSTimer *)aTimer;
@end
#import "Chronometer.h"
@implementation Chronometer
- (void) awakeFromNib
{
seconds = 0;
[NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector: @selector(seconds:)
userInfo: nil
repeats: YES];
}
}
@end
include $(GNUSTEP_MAKEFILES)/common.make
APP_NAME = Chronometer
Chronometer_MAIN_MODEL_FILE = Chronometer.gorm
Chronometer_RESOURCE_FILES = Chronometer.gorm
Chronometer_HEADER_FILES = Chronometer.h
Chronometer_OBJC_FILES = Chronometer.m
Chronometer_OBJC_FILES += AppMain.m
include $(GNUSTEP_MAKEFILES)/application.make
//Outlets
id textBox;
id button;
#import "Chronometer.h"
@implementation Chronometer
- (void) awakeFromNib
{
seconds = 0;
[button setState: NSOnState];
[NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector: @selector(seconds:)
userInfo: nil
repeats: YES];
}
}
@end
CGFloat x = punto.x;
CGFloat y = punto.y;
punto.x = 435;
s.width = 300;
// Obtenemos el ancho.
CGFloat ancho = rectangulo.size.width;
// Y el alto.
CGFloat alto = rectangulo.size.height;
// Cambiamo el alto.
rectangulo.size.height = 175;
// Obtenemos el tama~
no del rectangulo.
NSSize s = rectangulo.size;
#import <AppKit/AppKit.h>
#import <math.h>
#import "Clock.h"
@implementation Clock
- (void) awakeFromNib
{
NSRect frame = [self frame];
origin = NSMakePoint(frame.size.width/2,
frame.size.height/2);
end = NSMakePoint(0, 0.45*frame.size.height);
}
Capı́tulo 7: Otra app de ejemplo 96
end.x = length*sin(M_PI*seconds/30);
end.y = length*cos(M_PI*seconds/30);
@end
//Outlets
id textBox;
id button;
id clock;
#import "Clock.h"
#import "Chronometer.h"
@implementation Chronometer
- (void) awakeFromNib
{
seconds = 0;
[button setState: NSOnState];
Capı́tulo 7: Otra app de ejemplo 98
[NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector: @selector(seconds:)
userInfo: nil
repeats: YES];
}
include $(GNUSTEP_MAKEFILES)/common.make
APP_NAME = Chronometer
Chronometer_MAIN_MODEL_FILE = Chronometer.gorm
Chronometer_RESOURCE_FILES = Chronometer.gorm
Chronometer_HEADER_FILES = \
Clock.h \
Chronometer.h
Capı́tulo 7: Otra app de ejemplo 99
Chronometer_OBJC_FILES = \
Clock.m \
Chronometer.m
Chronometer_OBJC_FILES += AppMain.m
include $(GNUSTEP_MAKEFILES)/application.make
En la imagen 7-2 se muestra la misma app con una imagen a~ nadida como
fondo del reloj. La imagen se agrega primero al documento seleccionando
en el menú Document → Load Image. Seguidamente se agrega a la interfaz
un elemento imagen desde la paleta Data Palette. Y en el inspector, en el
campo con el tı́tulo Icon, se escribe el nombre de la imagen sin extensión.
O puede arrastrarse la imagen desde la sección Images del documento, para
soltarla sobre el elemento imagen. Luego seleccionando en el menú Layout
→ Send To Back, se envı́a la imagen al fondo para que no tape la aguja
del reloj. Las imágenes a~
nadidas a documentos de interfaz se guardan en el
archivo ‘gorm’, por lo que no deben agregarse al archivo ‘ǴNUmakefile’.
Capı́tulo 7: Otra app de ejemplo 100
8 Conceptos de dise~
no
En este capı́tulo veremos dos conceptos centrales en GNUstep que simpli-
fican el dise~
no de una app. Nos referimos a los métodos delegate y a las
notificaciones. También abordaremos el tema de los eventos del ratón y del
teclado en GNUstep. Ası́ como la forma de cargar otros archivos ‘gorm’ en
una app, lo que simplifica el desarrollo de la interfaz gráfica.
8.1 Delegate
delegate es un mecanismo para implementar ciertos métodos opcionales
los cuales nos permiten personalizar el comportamiento de un objeto. No
todos las clases tienen métodos delegate, solamente algunas de las clases
visuales ofrecen esta posibilidad. Entre las clases que proveen métodos
delegate están: NSText, NSTextView, NSTableView, NSOutlineView,
NSBrowser, NSWindow, NSApplication. Estos métodos pueden tener
diferentes propósitos, por ejemplo: obtener información a ser desplegada,
como en los objetos NSTableView; informar que una determinada acción
esta por ocurrir, o pedir aprobación para llevar a cabo dicha acción, o
también informar que dicha acción ya ha ocurrido. Los métodos delegate
que provee una clase en particular, se deben implementar en algún objeto
que haga de delegado (delegate). Y deben agregarse tanto en la interfaz
como en la implementación de la clase de dicho objeto.
Como hemos dicho, estos métodos son opcionales. Si GNUstep encuentra
que están implementados en un objeto delegado, los ejecuta. De lo contrario
no pasa nada. Puede surgir entonces la pregunta ¿Como sabe GNUstep
cuando están presentes estos métodos?. Objective-C es un lenguaje muy
dinámico, y nos permite preguntarle a un objeto si responde o no a un
método. El siguiente código muestra un ejemplo de esto:
En nuestra primera app que suma dos números, es evidente que las ca-
jas de texto (NSTextField) no deben permitir la inclusión de letras, sino
únicamente la de números. Esto podemos controlarlo utilizando el método
delegate -controlTextDidChange:, que se ejecuta cada vez que el contenido
de la caja de texto cambia. Para hacerlo, primero debemos indicarle a las dos
cajas de texto donde el usuario ingresa los números, que la instancia de nues-
tra clase SumaController será el delegado. Abramos nuestra interfaz gráfica
en Gorm y conectemos las cajas de texto con nuestro objeto SumaController,
seleccionando el outlet delegate en el Inspector. Hecho esto, agregamos el
método delegate a los archivos de interfaz e implementación de nuestra clase
SumaController. El código para este método es el siguiente:
Capı́tulo 8: Conceptos de dise~
no 102
- (void) awakeFromNib
{
[firstNumber setDelegate: self];
[secondNumber setDelegate: self];
}
Y es este objeto el que lleva la información del evento a los distintos objetos
de la aplicación, mientras se recorre la cadena de eventos. Sin embargo,
veamos antes algunos conceptos relacionados.
Los objetos NSWindow, que corresponden a las ventanas, utilizan varios
objetos NSView para construir la ventana. De todos estos, nosotros solamente
tenemos acceso al view conocido como content view. Es decir, al view que
dibuja el contenido de la ventana (el rectángulo entre la barra de titulo y los
bordes de la ventana). Es en este view donde se ubican los componentes de
nuestra ventana, botones, campos de texto, imágenes, etc. Se dice entonces
que estos componentes son subviews del content view. Y que el content view
es el superview de cada uno de estos componentes. Para entender mejor
esto, supongamos una ventana con dos componentes, como se muestra en la
imagen 8-1. Donde el objeto NSButton esta contenido dentro de un objeto
NSBox.
Pero para la caja de texto, el punto de inserción es lo que indica que esta es
el first responder.
NSScrollView *scrollView;
En este ejemplo, el TabView tiene una dimensión fija, por lo que no nos
preocupamos del ajuste automático de tama~ no. Sin embargo, si el Tab-
View fuera redimensionable, deberı́amos establecer las opciones de ajuste
automático. Por ejemplo, si el TabView puede cambiar tanto de ancho como
de alto, debemos establecer el ajuste de tama~ no tanto para el alto como
para el ancho del scrollView. El código en este caso podrı́a quedar de la
siguiente forma, solo se muestran las lineas relevantes:
Es habitual que varios ı́tems del menú estén conectados al objeto NSFirst.
En este caso, GNUstep comprueba si el objeto que sea el first responder, re-
sponde o no a los métodos en los ı́tems del menú, habilitando o inhabilitando
estos dependiendo de si los métodos están presentes o no en el first respon-
der. Por ejemplo, los ı́tems Cortar, Copiar y Pegar del menú de una app de
documentos, están conectados al objeto NSFirst. En la imagen 8-7, el menú
de la izquierda muestra el caso cuando no hay ningún documento abierto,
por lo que los tres ı́tems están inhabilitados. El siguiente menú, muestra
el caso cuando un documento tiene el foco y el pasteboard tiene contenido,
por lo que la opción Pegar esta habilitada. Y el caso mostrado en el tercer
menú, difiere del anterior en que se ha hecho una selección en el documento,
por lo que los ı́tems Cortar y Copiar están habilitados.
8.5 Notificaciones
Como ya hemos visto, los métodos delegados son muy útiles para personalizar
el comportamiento de nuestros objetos. Las notificaciones son similares a
estos métodos, pero con la diferencia de que podemos agregarlos a cualquier
objeto y para cualquier acción o evento ocurrido. Como su nombre lo indica,
son mensajes que notifican a un objeto de algo que ha ocurrido en otro
objeto. Una notificación se puede crear mediante la clase NSNotification, y
posteriormente enviarse mediante la clase NSNotificationCenter. O puede
crearse y enviarse directamente con esta última clase. Esta es la forma que
trataremos aquı́.
Toda notificación debe tener asignado un nombre y un objeto, general-
mente el objeto que manda la notificación. La notificación debe enviarse al
centro de notificaciones, que toda app o tool tiene por defecto. Por ejemplo,
para enviar una notificación llamada ColorDidChange que informa sobre un
cambio en el color, tendrı́amos:
[[NSNotification defaultCenter]
postNotificationName: @"ColorDidChange"
object: self];
[[NSNotification defaultCenter]
postNotificationName: @"ColorDidChange"
object: self;
userInfo: aDictionary];
[[NSNotificationCenter defaultCenter]
Capı́tulo 8: Conceptos de dise~
no 113
addObserver: self
selector: @selector(colorChanged:)
name: @"ColorDidChange"
object: nil];
El último parámetro indica el objeto del cual queremos recibir las noti-
ficaciones. En este ejemplo, hemos pasado como parámetro nil, indicando
con esto que queremos recibir todas las notificaciones ColorDidChange, sin
importar de que objetos provengan. Podemos agregar un objeto para recibir
cuantas notificaciones queramos. Pero debemos asegurarnos de removerlo
del centro de notificaciones, al momento de que sea liberado de la memoria.
Esto se hace en el método -dealloc. Por ejemplo:
- (void) dealloc
{
[[NSNotificationCenter defaultCenter]
removeObserver: self];
[super dealloc];
}
...
...
...
}
outlet que conectara con la paleta de colores. Veamos entonces el código del
archivo de implementación:
#import "PPController.h"
@implementation PPController
- (void) awakeFromNib
{
/* Por defecto se selecciona la
herramienta de trazos. */
tool = 0;
}
- (NSInteger) tool
{
return tool;
}
@end
#import <AppKit/AppKit.h>
// Outlet
id colorWell;
}
- (void) awakeFromNib;
- (id) colorWell;
- (void) setTool: (id)sender;
- (NSInteger) tool;
@end
Nuestro editor gráfico debe manejar, por supuesto, imágenes. Por sim-
plicidad, le daremos soporte únicamente para archivos JPG. Toda app de
documentos debe tener una subclase de la clase NSDocument, la que, entre
otras cosas, se encargará de decirle a la app como leer y guardar los archivos
soportados. Si una app va a soportar diferentes tipos de archivos puede ser
necesario, dependiendo de la complejidad, tener una subclase NSDocument
para cada tipo soportado. En otros casos, una sola subclase puede ser su-
ficiente para manejar todos los tipos de archivos. Para nuestra app Power-
Paint, crearemos una subclase llamada PPDocumentClass, que tendrá una
variable de instancia para almacenar el archivo leı́do por el usuario (rep), y
un outlet hacia el objeto NSView que se encargará de mostrar la imagen en
la ventana (view). La implementación de esta clase es la siguiente:
#import <AppKit/AppKit.h>
#import "PPDocumentView.h"
#import "PPDocumentClass.h"
@implementation PPDocumentClass
- (id) init
{
self = [super init];
if (self)
{
rep = nil;
}
Capı́tulo 9: Una app de documentos 118
return self;
}
- (NSString *) windowNibName
{
return @"Document";
}
if (rep != nil)
{
return YES;
}
else
{
return NO;
}
}
- (void) windowControllerDidLoadNib:
(NSWindowController *)windowController
{
[view setFile: [rep autorelease]];
}
@end
#import <AppKit/NSDocument.h>
// Outlet
id view;
}
@end
#import <AppKit/NSView.h>
@class PPDocumentClass;
// Outlet
PPDocumentClass *document;
}
@end
Capı́tulo 9: Una app de documentos 121
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "PPController.h"
#import "PPDocumentClass.h"
#import "PPDocumentView.h"
@implementation PPDocumentView
- (void) awakeFromNib
{
lastFrame = nil;
selection = NO;
}
// Redefinicion
- (BOOL) acceptsFirstResponder
{
return YES;
}
// Redefinicion
- (BOOL) acceptsFirstMouse: (NSEvent *)theEvent
{
return YES;
}
Capı́tulo 9: Una app de documentos 122
- (NSBitmapImageRep *) currentContent
{
NSBitmapImageRep *bitmap;
return bitmap;
}
// Redefinicion.
- (void) mouseDown: (NSEvent *)theEvent
{
NSBitmapImageRep *bitmap = [self currentContent];
// Redefinicion
- (void) mouseDragged: (NSEvent *)theEvent
{
locB = [self convertPoint: [theEvent locationInWindow]
Capı́tulo 9: Una app de documentos 124
fromView: nil];
NSSize size = NSMakeSize(locB.x - locA.x,
locB.y - locA.y);
selectedRect.size = size;
// Redefinicion
- (void) mouseUp: (NSEvent*)theEvent
{
ASSIGN(lastFrame, [self currentContent]);
selection = NO;
}
// Redefinicion
- (void) drawRect: (NSRect)rect
{
if (lastFrame != nil)
{
[lastFrame drawInRect: [self frame]];
}
else
{
[[NSColor whiteColor] set];
[[NSBezierPath bezierPathWithRect: rect] fill];
}
if (selection)
{
NSBezierPath *path;
[[[[NSApp delegate] colorWell] color] set];
// Redefinicion
- (void) dealloc
{
[lastFrame release];
[super dealloc];
}
@end
#import <AppKit/NSPanel.h>
Y la implementación:
#import "PPToolsPanel.h"
@implementation PPToolsPanel
- (BOOL) canBecomeKeyWindow
{
return NO;
}
@end
Y con esto tenemos los archivos de código necesarios para nuestra app.
Es momento de crear la interfaz gráfica.
{
switch ([anItem tag])
{
case 1:
{
// Instrucciones.
}
break;
case 2:
{
// Instrucciones.
}
break;
case 3:
{
// Instrucciones.
}
break;
}
}
#import <AppKit/AppKit.h>
int
main(int argc, const char *argv[])
{
return NSApplicationMain (argc, argv);
}
include $(GNUSTEP_MAKEFILES)/common.make
# Application
APP_NAME = PowerPaint
PowerPaint_MAIN_MODEL_FILE = PowerPaint.gorm
# Resource files
PowerPaint_RESOURCE_FILES = \
PowerPaint.gorm \
Document.gorm
# Header files
PowerPaint_HEADER_FILES = \
PPController.h \
Capı́tulo 9: Una app de documentos 133
PPDocumentClass.h \
PPDocumentView.h \
PPToolsPanel.h
# Class files
PowerPaint_OBJC_FILES = \
PPController.m \
PPDocumentClass.m \
PPDocumentView.m \
PPToolsPanel.m
# Other sources
PowerPaint_OBJC_FILES += \
PPMain.m
# Makefiles
-include GNUmakefile.preamble
include $(GNUSTEP_MAKEFILES)/aggregate.make
include $(GNUSTEP_MAKEFILES)/application.make
-include GNUmakefile.postamble
{
ApplicationName = PowerPaint;
ApplicationDescription = "My first document app!";
ApplicationRelease = "0.1";
Copyright = "Copyright (C) 2013";
CopyrightDescription = "Released under the GPLv3";
NSTypes = (
{
NSName = "jpg";
NSHumanReadableName = "Imagen JPG";
NSIcon = "FileIcon_jpg.tiff";
NSUnixExtensions = ( jpg );
NSDOSExtensions = ( jpg );
NSRole = Editor;
NSDocumentClass = PPDocumentClass;
}
);
}
Capı́tulo 9: Una app de documentos 134
Los nombres de las claves en este diccionario, son los valores que por de-
fecto se buscarán en este archivo. Los primeros cinco datos, son información
general sobre la app que aparecerá en el panel de información de esta (opción
del menú Info → Info Panel...). Obsérvese que se utilizan comillas dobles en
aquellos datos que contienen espacios.
Como se ve, este archivo es muy importante para que funcione toda la
maquinaria de una app de documentos. Este archivo no debe agregarse en
el ‘GNUmakefile’, ya que la herramienta gnustep-make lo busca siempre que
se compila un proyecto.
Por último, solo nos queda compilar y probar nuestra app, imagen 9-7.
Compruébese el funcionamiento de las herramientas, de la paleta de colores
y de las distintas opciones del menú.
Capı́tulo 9: Una app de documentos 135
10 Proyectos en GNUstep
En este capı́tulo veremos como hacer que una app este disponible en varios
idiomas. Ası́ como la forma en que podemos organizar nuestros proyectos,
para el caso cuando estos contienen muchos archivos y se vuelve necesario
establecer un orden en el mismo. Por último, veremos algunas opciones
adicionales que podemos agregar a los archivos ‘Info.plist’.
10.1 Internacionalización
La internacionalización se refiere a cuando una aplicación esta disponible
en más de un idioma. Esto se logra agregando carpetas para los distin-
tos idiomas disponibles e indicando esto en el archivo GNUmakefile. Estas
carpetas deben llevar por nombre el nombre del idioma correspondiente, el
nombre estandarizado, y la extensión ‘lproj’. Y deben contener el mismo
número de archivos con los mismos nombres. Con la diferencia de que en
cada carpeta, el contenido de estos debe estar en el respectivo idioma. A con-
tinuación se muestran los nombres de estas carpetas para algunos idiomas:
English.lproj
Spanish.lproj
German.lproj
French.lproj
NSRunAlertPanel(_(@"Error"),
_(@"An error occurred when copy the file."),
_(@"OK"), nil, nil);
/***
Spanish.lproj/Localizable.strings
updated by make_strings 2010-10-21 19:00:31 -0600
add comments above this one
***/
/* File: Board.m:753 */
/* Flag: untranslated */
/* File: Board.m:777 */
/* Flag: untranslated */
/* File: Board.m:783 */
/* Flag: untranslated */
"Game Over" = "Fin del juego";
/* File: main.m:57 */
/* Flag: untranslated */
/* File: main.m:60 */
/* Flag: untranslated */
"Info" = "Informaci\U00F3n";
include $(GNUSTEP_MAKEFILES)/common.make
# Nombre de la app.
APP_NAME = PowerPaint
# Makefiles.
include $(GNUSTEP_MAKEFILES)/application.make
Authors = (
"Germ\U00E1n Arias"
);
NSBuildVersion = "March 2013";
URL = www.ejemplo.com;
Authors = (
"Germ\U00E1n Arias",
Fulano
);
Obsérvese que al igual que en los archivos de cadenas de texto, los car-
acteres que no pertenecen al inglés deben escribirse mediante su código cor-
respondiente.
Por supuesto, estos archivos también pueden utilizarse en apps que no
sean de documentos.
Capı́tulo 11: Apariencia y portabilidad 142
11 Apariencia y portabilidad
En este capı́tulo consideraremos primero los aspectos a tener en cuenta si
queremos que nuestras apps soporten el estilo de menú empotrado en la
ventana (NSWindows95InterfaceStyle). Ası́ como aquellas relativas a la
portabilidad de nuestros programas. Por ejemplo, si desarrollamos nuestro
programa en un sistema GNU/Linux y queremos que este se ejecute en un
sistema Windows y/o Mac OS X.
11.1 Apariencia
Por defecto GNUstep utiliza el menú vertical estilo NextStep
(NSNextStepInterfaceStyle). Pero podrı́a ser que quisiéramos
darle soporte a nuestra app para utilizar un menú empotrado en la ventana
(NSWindows95InterfaceStyle). Consideremos primero el caso de una app
que no es de documentos. En este caso, el principal requisito es que la app
provea una ventana para colocar el menú. Para el caso en que nuestra
app no muestra una ventana al momento en que es lanzada, deberemos
asegurarnos de hacer visible alguna ventana para que el menú pueda ser
insertado en ella. Si nuestra app muestra mas de una ventana, deberemos
elegir una de estas para colocar el menú. Aunque lo recomendable es que
una app tenga solamente una ventana principal y que el resto sean paneles
(NSPanel). Por defecto, los paneles no pueden albergar un menú.
Cuando se utiliza el estilo NSWindows95InterfaceStyle, GNUstep
agrega el menú en aquellas ventanas cuyos métodos -canBecomeMainWindow
devuelven YES. Ya que el menú debe estar presente solamente en una
ventana, deberemos asegurarnos que el resto de ventanas, en caso de
haberlas, retornen NO en sus métodos -canBecomeMainWindow. Para
realizar esto, debemos crear una subclase de la clase NSWindow, para
redefinir el método -canBecomeMainWindow de tal forma que retorne NO. Y
hacer que el resto de ventanas sean una instancia de esta clase. La interfaz
de dicha clase, que aquı́ llamamos OtherWindow, seria:
#include <AppKit/NSWindow.h>
Y su correspondiente implementación:
#include "OtherWindow.h"
@implementation OtherWindow
Capı́tulo 11: Apariencia y portabilidad 143
- (BOOL) canBecomeMainWindow
{
return NO;
}
@end
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:
(id)sender
{
if (NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil)
== NSWindows95InterfaceStyle)
{
// Cerramos la app solo si la ventana con el menu
// ha sido cerrada.
if (mainWindow == nil)
{
return YES;
}
else
{
return NO;
}
}
else
{
// Con un menu independiente no cerramos la app.
return NO;
}
}
- (BOOL) applicationShouldOpenUntitledFile:
(NSApplication *)sender
{
if (NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil)
== NSWindows95InterfaceStyle)
Capı́tulo 11: Apariencia y portabilidad 146
{
return YES;
}
else
{
return NO;
}
}
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:
(id)sender
{
if (NSInterfaceStyleForKey(N̈SMenuInterfaceStyle", nil)
== NSWindows95InterfaceStyle)
{
NSDocumentController *docController =
[NSDocumentController sharedDocumentController];
11.2 Portabilidad
La principal consideración a tomar en cuenta es en lo referente al sistema
Mac OS X. Aunque GNUstep puede instalarse en este sistema operativo,
y de esta forma ejecutar nuestras apps usando GNUstep en Mac OS X, no
hay paquetes actualizados de GNUstep para este sistema. Por lo que la
instalación debe realizarse desde el código fuente. Esto, por supuesto, com-
plica la instalación de nuestras apps. De esta forma, lo común es que las
apps se ejecuten utilizando los frameworks de Cocoa. El principal problema
con esto, es que Cocoa no puede manejar los archivos ‘gorm’, los cuales son
exclusivos de GNUstep. Una solución es abrir los archivos de interfaz en
Capı́tulo 11: Apariencia y portabilidad 148
http://www.gnustep.it/Renaissance/
• pwd
Nos muestra la ruta en la que estamos ubicados.
• ls
Nos muestra los archivos en la ubicación actual.
• cd <nombre>
Nos permite ingresar a la carpeta cuyo nombre/ruta se pase como
parámetro.
• cd ..
Sale de la carpeta actual y nos ubica un nivel arriba en el árbol de
carpetas.
• cd
Nos ubica en nuestra carpeta de usuario.
• mkdir <nombre>
Crea una carpeta, en la ubicación actual, con el nombre que se pase
como parámetro.
• rm <nombre>
Borra el archivo, en la ubicación actual, cuyo nombre se pase como
parámetro.
Apéndice A: Introducción al Shell 151
• rm -rf <nombre>
Borra la carpeta, en la ubicación actual, cuyo nombre se pase como
parámetro, incluyendo todo su contenido.
man ls
• make
Compila el proyecto en la carpeta actual. Si se encuentra un error,
se mostrará el nombre del archivo y el número de linea donde se en-
contró el error. Dicho error debe corregirse antes de intentar compilar
nuevamente el proyecto. A veces se muestran también advertencias
(warnings) sobre posibles errores. En estos casos, queda a criterio del
programador determinar si se trata de errores o no.
• make clean
Elimina lo creado por la compilación (lo inverso a compilar).
• make distclean
Similar al anterior, pero también elimina lo creado por el script de
configuración.
• make install
Instala la App/Tool en el sistema. Generalmente se requieren permisos
de superusuario para poder copiar los archivos a las carpetas del sistema.
La obtención de dichos permisos puede variar de un sistema a otro.
En el caso de Windows se requiere que el usuario tenga permisos de
administrador para instalar programas.
• make uninstall
Desinstala la App/Tool del sistema. Generalmente se requieren per-
misos especiales para llevar a cabo esta acción.
• make dist
Crea un paquete tar.gz con todos los archivos del proyecto, con el
nombre y la versión del programa que estén indicados en el archivo
‘GNUmakefile’. Ver capı́tulo 10.
• make nsis
Crea un instalador para Windows, con el nombre y la versión del pro-
grama que estén indicados en el archivo ‘GNUmakefile’. Únicamente
para proyectos de apps. Véase el documento Empaquetando para Win-
dows para mayor información.
. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
to related matters) and contains nothing that could fall directly within
that overall subject. (Thus, if the Document is in part a textbook of
mathematics, a Secondary Section may not explain any mathematics.)
The relationship could be a matter of historical connection with the
subject or with related matters, or of legal, commercial, philosophical,
ethical or political position regarding them.
The “Invariant Sections” are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice that
says that the Document is released under this License. If a section
does not fit the above definition of Secondary then it is not allowed to
be designated as Invariant. The Document may contain zero Invariant
Sections. If the Document does not identify any Invariant Sections then
there are none.
The “Cover Texts” are certain short passages of text that are listed, as
Front-Cover Texts or Back-Cover Texts, in the notice that says that the
Document is released under this License. A Front-Cover Text may be
at most 5 words, and a Back-Cover Text may be at most 25 words.
A “Transparent” copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the general
public, that is suitable for revising the document straightforwardly with
generic text editors or (for images composed of pixels) generic paint pro-
grams or (for drawings) some widely available drawing editor, and that
is suitable for input to text formatters or for automatic translation to
a variety of formats suitable for input to text formatters. A copy made
in an otherwise Transparent file format whose markup, or absence of
markup, has been arranged to thwart or discourage subsequent modi-
fication by readers is not Transparent. An image format is not Trans-
parent if used for any substantial amount of text. A copy that is not
“Transparent” is called “Opaque”.
Examples of suitable formats for Transparent copies include plain ASCII
without markup, Texinfo input format, LaTEX input format, SGML or
XML using a publicly available DTD, and standard-conforming simple
HTML, PostScript or PDF designed for human modification. Examples
of transparent image formats include PNG, XCF and JPG. Opaque
formats include proprietary formats that can be read and edited only
by proprietary word processors, SGML or XML for which the DTD
and/or processing tools are not generally available, and the machine-
generated HTML, PostScript or PDF produced by some word processors
for output purposes only.
The “Title Page” means, for a printed book, the title page itself, plus
such following pages as are needed to hold, legibly, the material this
License requires to appear in the title page. For works in formats which
do not have any title page as such, “Title Page” means the text near the
most prominent appearance of the work’s title, preceding the beginning
of the body of the text.
Apéndice D: GNU Free Documentation License 158
I. Preserve the section Entitled “History”, Preserve its Title, and add
to it an item stating at least the title, year, new authors, and
publisher of the Modified Version as given on the Title Page. If
there is no section Entitled “History” in the Document, create one
stating the title, year, authors, and publisher of the Document as
given on its Title Page, then add an item describing the Modified
Version as stated in the previous sentence.
J. Preserve the network location, if any, given in the Document for
public access to a Transparent copy of the Document, and likewise
the network locations given in the Document for previous versions
it was based on. These may be placed in the “History” section. You
may omit a network location for a work that was published at least
four years before the Document itself, or if the original publisher of
the version it refers to gives permission.
K. For any section Entitled “Acknowledgements” or “Dedications”,
Preserve the Title of the section, and preserve in the section all the
substance and tone of each of the contributor acknowledgements
and/or dedications given therein.
L. Preserve all the Invariant Sections of the Document, unaltered in
their text and in their titles. Section numbers or the equivalent are
not considered part of the section titles.
M. Delete any section Entitled “Endorsements”. Such a section may
not be included in the Modified Version.
N. Do not retitle any existing section to be Entitled “Endorsements”
or to conflict in title with any Invariant Section.
O. Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or appendices
that qualify as Secondary Sections and contain no material copied from
the Document, you may at your option designate some or all of these
sections as invariant. To do this, add their titles to the list of Invariant
Sections in the Modified Version’s license notice. These titles must be
distinct from any other section titles.
You may add a section Entitled “Endorsements”, provided it contains
nothing but endorsements of your Modified Version by various parties—
for example, statements of peer review or that the text has been ap-
proved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list of
Cover Texts in the Modified Version. Only one passage of Front-Cover
Text and one of Back-Cover Text may be added by (or through ar-
rangements made by) any one entity. If the Document already includes
a cover text for the same cover, previously added by you or by arrange-
ment made by the same entity you are acting on behalf of, you may not
Apéndice D: GNU Free Documentation License 161
add another; but you may replace the old one, on explicit permission
from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or imply
endorsement of any Modified Version.
5. COMBINING DOCUMENTS
You may combine the Document with other documents released under
this License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the Invari-
ant Sections of all of the original documents, unmodified, and list them
all as Invariant Sections of your combined work in its license notice, and
that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single copy.
If there are multiple Invariant Sections with the same name but different
contents, make the title of each such section unique by adding at the end
of it, in parentheses, the name of the original author or publisher of that
section if known, or else a unique number. Make the same adjustment
to the section titles in the list of Invariant Sections in the license notice
of the combined work.
In the combination, you must combine any sections Entitled “History”
in the various original documents, forming one section Entitled “His-
tory”; likewise combine any sections Entitled “Acknowledgements”, and
any sections Entitled “Dedications”. You must delete all sections Enti-
tled “Endorsements.”
6. COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other docu-
ments released under this License, and replace the individual copies of
this License in the various documents with a single copy that is included
in the collection, provided that you follow the rules of this License for
verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute
it individually under this License, provided you insert a copy of this
License into the extracted document, and follow this License in all other
respects regarding verbatim copying of that document.
7. AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an “aggregate” if the copyright resulting
from the compilation is not used to limit the legal rights of the com-
pilation’s users beyond what the individual works permit. When the
Document is included in an aggregate, this License does not apply to
the other works in the aggregate which are not themselves derivative
works of the Document.
Apéndice D: GNU Free Documentation License 162
Índice