Objetos distribuidos en Cocoa usando NSConnection y NSProxy

diciembre 11, 2010

Cuando nuestros programas crecen lo suficiente, nos encontramos con situaciones donde tenemos varios hilos de ejecución o incluso diferentes procesos que se ejecutan de forma paralela en la misma o en diferente máquina. En estas situaciones, es habitual tener que comunicar estos diferentes módulos.

Una típica forma de comunicación es el uso de objetos “distribuidos”, desde los vetustos (y terroríficos :) ) objetos CORBA, pasando por RMI de Java o Remoting de .NET, hasta los mas modernos WebServices, estos mecanismos han sido y son ampliamente utilizados.

Explicado de una forma muy simple, estos mecanismos permiten llamar a un método de una instancia de un objeto que no se esta ejecutando en el mismo entorno que el llamador. Ese entorno puede ser otro hilo, otro proceso u otra máquina.

Como era de esperar, Cocoa plantea su aproximación al uso de objetos distribuidos. Como hemos hablado anteriormente en el blog, el mecanismo de invocación de métodos en Objetive-c basado en mensajes, ayuda a entender y a enfocar de otra manera el funcionamiento de la invocación remota de métodos de objetos.

En un lenguaje como Java, cuando invocas a un método remoto, realmente estas invocando a un método que tiene que existir en una clase “local”. Esta clase “local” habitualmente es un Proxy que se autogenera estáticamente. El Proxy por su parte, transforma la llamada en una comunicación con el servidor remoto, que se encarga de invocar el método y devolver el retorno.

En Objetive-C, una llamada a un objeto remoto, se traduce en un mensaje que se “enruta” hasta llegar al objeto receptor. En este caso, el Proxy no es necesario autogenerarlo, pues como hablamos, el Proxy será un objeto que es capaz de recibir en tiempo de ejecución cualquier tipo de mensaje. No tenemos la necesidad de que el método realmente exista para poder compilar nuestro código.

Las clases que entran en juego son numerosas, y el funcionamiento completo tiene su “intríngulis” :) , por lo que en este post nos centraremos en el aspecto mas práctico, es decir, poner un ejemplo de como funciona.

El código se divide en dos partes, por un lado esta el objeto que va e estar ejecutandose en modo “servidor” y por otro, el lado del cliente.

Hablando del servidor, vamos a tener un objeto de tipo “Calculadora” que va a tener un método “addNumber:with:”, que como dice su nombre, sumará dos números. A este objeto lo vamos a llamar de forma remota.

@protocol CalculatorProtocol
- (int) addNumber:(int) a with:(int)b;
@end

@interface Calculator : NSObject<CalculatorProtocol> {
}
@end

El protocolo, nos va a ayudar a que el proxy sepa que el método existe. Realmente esto no es estrictamente necesario, ya que el mensaje se enrutará de todas maneras hasta llegar a la clase Calculator, pero sin embargo es una buena ayuda de cara a la comprobación que el runtime hace para saber si la clase remota responde o no al mensaje.

Una vez que tenemos el objeto que queremos exportar, necesitamos publicarlo, o como se llama en la documentación: “Vending an Object”.

Para ello, vamos a hacer uso de la clase NSConnection

Calculator* calculator = [[Calculator alloc] init];
NSConnection* theConnection = [[[NSConnection alloc] init] autorelease];
[theConnection setRootObject:calculator];
[theConnection registerName:@"calculator"];

[[NSRunLoop currentRunLoop] run]; 
 // Solo si nuestra aplicación no lo ha hecho ya
 // En caso de una aplicación Cocoa, esto no es necesario

Con esas 5 lineas, nuestro Servidor ya estará escuchando peticiones, y el objeto estará listo para usarse desde otro entorno de ejecución, fácil, ¿no?.

La parte cliente, no es que sea mucho mas compleja ;)

id theProxy;
theProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:@"calculator" host:nil] retain];
[theProxy setProtocolForProxy:@protocol(CalculatorProtocol)];
NSLog(@"%d", [theProxy addNumber:5 with:3]);

Una vez mas el objeto NSConnection nos va a servir para obtener la conexión al objeto remoto. Usamos la misma cadena que se utilizó en el registro, y pasamos “nil” al parámetro host, ya que en este ejemplo, el servidor esta en la misma máquina.

Ambos fragmentos de código se ejecutan en procesos diferentes, y como se espera, el resultado del cliente es imprimir un 8 en la pantalla de logs.

Sin duda, el post no ataca toda la complejidad que hay detrás de los mecanismos, y las diferentes alternativas que existen de cara a la configuración de la conexión u otros aspectos que entran en juego, pero aún así se puede ver lo sencillo que es invocar un mensaje sobre un objeto que esta ejecutandose fuera de nuestro proceso.


E4X, Javascript y XML

noviembre 13, 2010

Hace un tiempo, acceder a documentos XML era perfectamnete posible, pero había que pasar por APIs que quizás no eran todo lo sencillas que deberían ser.

Las dos principales APIs que nos servían para procesar un documento XML eran DOM y SAX. Los nombres describían la forma de acceder al documento, mientras que en DOM se cargaba todo el documento en memoria, y podiamos acceder de una forma más sencilla, en SAX registrabamos una serie de “callbacks” a los que se nos iba llamando segun se procesaba el documento.

Sin duda DOM era mas sencilla desde el punto de vista del uso, pero menos eficiente, si nuestro documento era muy grande, necesitabamos tenerlo completamente en memoria. Por su lado SAX era más eficiente, pero la forma de desarrollar con él era bastante incomoda, ya que teniamos que separar nuestro código en multiples funciones de callback y nosotros teniamos que ser responsables de mantener el estado entre las diferentes llamadas.

Sin duda, hoy en día XML es el lenguaje de intercambio de datos más extendido (ya veremos en unos pocos años si esto sigue siendo así). Y sobre todo teniendo en cuenta que en el entorno del navegador, usar XML suele (o solía) ser común para intercambiar datos usando AJAX.

Debido a todo esto, poco a poco los lenguajes han ido incorporando mecanismos cada vez más comodos para procesar los documentos XML, como por ejemplo en Java JAXP . En este caso vamos a hablar sobre E4X, o la extensión de ECMAScript para el procesado de documentos XML.

Tratar un documento XML con esta extensión es sumamente sencillo, de hecho, el propio código habla por si mismo.

var library = <library postalcode="28000">
<books>
<book id="1">
  <title>Book Title 1</title>
  <author>Author of Book 1</author>
</book>
<book id="2">
  <title>Book Title 2</title>
  <author>Other Author of Book 2</author>
</book>
</books>
<magazines>
<magazine id="1">
<title>Magazine title 1</title>
</magazine>
</magazines>
</library>

alert(library.books.book[0].title); // Book Title 1
alert(library.books.book.(@id == "2").author); // Other Author of Book 2

Como veis, el procesado de un documento XML se simplifica de una manera dramática. Al mapear un documento XML a un objeto, su forma de acceso es practicamente similar a como lo haríamos con dicha estructura de datos.

La sintaxis de acceso al documento nos recuerda en cierta manera a XPath, pudiendo hacer cosas como:

alert(library.books..*.length()) 
	// El operador .. accede a todos los hijos.
alert(library.magazines.magazine[0].@id) 
	// Usando el operador @ accedemos a los atributos
alert(library.books.book.(@id == "2").author); 
	// Podemos filtrar por el valor de los atributos

Además de ayudar a procesar un documento, tambien tenemos facilidad para generar XML, si “encerramos” una expresion entre {}, se sustituirá por su valor, o incluso usando el operador +, añadiremos nuevos elementos al documento.

var b = "book"
var xmlDoc = <{b}><title>The Title</title></{b}>
alert(xmlDoc.toXMLString())

library.books.book += <book id="3"><title>....</book>

Después de ver todo lo que E4X puede hacer por nosotros, ahora contaremos la parte mala :) .

Actúalmente, solo esta soportado en productos de Mozilla, esto es, Firefox, Thunderbird o Rhino, además de ActionScript, pero no se puede utilizar en Chrome, Safari o Internet Explorer. Y, ¿Por qué sucede esto?, pues básicamente por que los desarrolladores de los navegadores no consideran que sea necesaria esta implementación, ya que consideran que el acceso estandar usando DOM en Javascript es lo suficientemente versatil como para no necesitar nada más. Además, librerías como JQuery tienen extensiones para manejar documentos XML.

En cualquier caso, al ser un estandar nos garantizamos que si algun día deciden incorporar E4X en los navegadores actúalmente no soportados, funcionará de forma identica a la que lo hace ahora mismo en Firefox o incluso en ActionScript.


Aplicación de escritorio usando Javascript

noviembre 1, 2010

El otro día me preguntaba si seria posible hacer una aplicación de escritorio usando Javascript. Como sabemos, Javascript es uno de los lenguajes que mas peso ha tenido en los últimos años, y que ha tenido que sufrir la guerra de navegadores e implementaciones a la que hemos sido testigos recientemente.

Sin embargo, Javascript es un lenguaje fascinante, con aspectos muy avanzados y que poco a poco ha ido añadiendo cosas cada vez mas interesantes, como por ejemplo sus interesantes mecanismos de introspección y ejecución de código de forma dinámica.

Pero, en esta entrada nos vamos a centrar en como hacer una aplicación de escritorio usando Javascript, cosa que seguramente no sea muy habitual.

Para ello, nos vamos a valer de la plataforma con la que mas se confunde Javascript y que nada tiene que ver, si, estamos hablando de Java. Aun recuerdo las noches que pasaba por el IRC, mas concretamente en el canal #java, la cantidad de gente que entraba a preguntar cosas sobre javascript era tremenda. Lo cierto es que el nombre no ayuda, y la confusión es bastante probable.

Aunque hay varias opciones, he elegido Java como opción ya que aporta varias cosas importantes, por un lado es su gran librería gráfica Swing, que esta integrada en la plataforma, por otro lado su característica mas destacable, la multiplaformidad, lo mismo se ejecuta en Linux, OSX o Windows. Estas dos cosas son ventajas en su mismas de Java, lo que actúa como nexo de unión es Rhino (http://www.mozilla.org/rhino/).

Rhino es una librería hecha por mozilla, que permite integrar Javascript totalmente en la plataforma de Java. Lo cierto es que Rhino esta integrado desde la versión 1.6 dentro de Java, por lo que si queremos interpretar código javascript en Java, no necesitamos nada mas que la propia JDK, sin embargo, en el paquete de rhino, aparte de la librería, viene un interprete interactivo de javascript que es bastante útil.

Lo cierto es que gracias a la integración entre Java y Javascript, hacer una aplicación de escritorio es bastante sencillo. Si por ejemplo ejecutamos el siguiente programa:

var frame = new javax.swing.JFrame();
frame.setSize(200,200);
frame.setVisible(true);
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

Usando el interprete de rhino:

java -jar js.jar prueba.js

Obtendremos, como esperamos, una ventana vacía.

Como vemos, este método es sencillo y bastante potente para realizar prototipos, ya que podemos probar cualquier código java, usando las comodidades de Javascript y quitandonos el paso de la compilación.

Incluso, podremos usar la consola interactiva de Javascript, para ir modificando la interfaz poco a poco, e ir viendo los cambios sobre la marcha.

Sin duda, uno de los beneficios de Javascript que aprovechamos en esta forma de interactuar con swing son la posibilidad de definir una función en la llamada que añade los listeners a los controles. Así por ejemplo, si tenemos un botón, podemos definir el código controlador que se lanza al pulsarlo, en la propia llamada al método addActionListener.

Como en el siguiente código:

function main() {
	var frame = new javax.swing.JFrame();
	frame.setSize(200,200);
	frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
	
	var button = new javax.swing.JButton("Push Me!");
	var label = new javax.swing.JLabel("JLabel");
	var panel = new javax.swing.JPanel();
	
	button.addActionListener(function() {
		label.setText("Pressed!")
	});
	
	panel.add(button);
	panel.add(label);
	
	frame.getContentPane().add(panel);
	
	frame.setVisible(true);
}

Después de ver todo esto, queda respondida la duda que iniciaba el post. Si, se pueden hacer aplicaciones de escritorio usando Javascript, claro que con la inestimable ayuda de Java y Rhino :)


HTML5 y Bases de Datos locales

octubre 16, 2010

Otra de las grandes novedades de HTML5 es las nuevas alternativas respecto a la forma de guardar información en el cliente por parte de una web. Hasta ahora, la única alternativa que se tenia desde una web era usar cookies. HTML5 introduce tres nuevas formas, “local” y “session” storage y por otro lado la posibilidad de manejar toda una base de datos relacional que reside en cada cliente. A diferencia del localstorage o sessionstorage, que son mas similares a las cookies, el hecho de manejar una base de datos totalmente funcional es una novedad bastante importante que puede ayudar a nuestras webs a guardar información de una forma mucho mas estructurada.

Como hemos dicho, la base de datos es de tipo relacional, por lo que el lenguaje para “hablar” con ella será enteramente SQL.

Aunque expresamente la W3 desaconseja ligar el estándar a una implementación concreta, en la practica, prácticamente la totalidad de navegadores han usado SQL lite para implementar la gestión de la base de datos.

Como sabemos, no el SQL que entiende todas las bases de datos es igual, existen lo que se denominan “dialectos”, debido a que SQL lite es casi la opción “de-facto” podremos usar el “dialecto” del SQL lite en las sentencias. Aunque, al igual que el estándar desaconsejaba ligarse a una implementación, no es buena idea ligar el código a un dialecto, pues puede ser que en algún momento uno de los navegadores decida utilizar otro “backend” de bases de datos, y haga nuestro código incompatible. Lo ideal es usar, en la medida de lo posible, el SQL mas cercano al estándar.

Pese a todo esto, de momento y hasta que no se plantee una implementación independiente, el estándar de HTML5 respecto al “WebSQL” apunta a que el dialecto a seguir es del SQL lite.

Pasando ya a como usar la base de datos desde javascript, el primer paso es obtener una referencia a la base de datos para poder operar con ella. Para ello usaremos la función openDatabase() donde le pasaremos, el nombre de la base de datos, la versión del esquema y el tamaño estimado.

Una vez tenemos la referencia de la base de datos, vamos a ver que, prácticamente la totalidad de los métodos son asíncronos, por lo que típicamente los argumentos serán callbacks. En este caso Javascript sale a nuestra ayuda, como seguramente sabremos, en Javascript podemos encapsular en una variable la declaración de una función, por lo que la forma mas común de pasar los parámetros a las funciones que operan en la base de datos es incluir la declaración de los propios callbacks, ya que habitualmente, no será muy útil reutilizar dichos callbacks.

Las dos funciones más usadas sin duda van a ser “transaction” y “executeSql”, con la primera crearemos una transacción que se ejecutará contra la base de datos de forma atómica (bueno, esa es la definición de transacción, no?), mientras que con la segunda, como su nombre indica ejecutaremos las propias sentencias sql.

A continuación veremos un simple ejemplo en el que creamos una base de datos de usuarios en el cliente, donde guardamos cosas como su nombre de usuario o password.

<html>
<head>
<script type="text/javascript">
    var db;
    function init() {
        db = openDatabase("DB Prueba", "0.1", "Database Prueba", 200000);
        if (db) {
            // Database opened
			db.transaction( function(tx) {
				tx.executeSql("CREATE TABLE IF NOT EXISTS usuarios(userid integer primary key autoincrement, username text, password text)")
			});
        }

		listUsers();

    }

	function showUsers(users) {
		var place = document.getElementById("usersDiv");
		if (place.getElementsByTagName("ul").length > 0 )
			place.removeChild(place.getElementsByTagName("ul")[0]);
		var list = document.createElement("ul");

		for ( var i = 0; i < users.length; i++) {
			var item = document.createElement("li");
			item.innerHTML += "<b>userId:</b>" + users[i][0] + " <b>userName:</b>"
					+ users[i][1] + " <b>password:</b>" + users[i][2] +
					"<button onclick='removeUser("+ users[i][0]+")'>Remove</button>";
			list.appendChild(item);
		}
		place.appendChild(list);
	}

	function listUsers() {
		db.transaction( function(tx) {
			tx.executeSql("SELECT * FROM usuarios", [],
				function(tx, result){
					var output = [];
					for(var i=0; i < result.rows.length; i++) {
						output.push([result.rows.item(i)['userid'],
								result.rows.item(i)['username'],
								result.rows.item(i)['password']]);
					}

					showUsers(output);

				});
		});
	}

	function addUser(username, password) {
		db.transaction( function(tx) {
			tx.executeSql("INSERT INTO usuarios(username, password) VALUES(?,?)", [username, password]);
		});
		listUsers();
	}

	function removeUser(userId) {
		db.transaction(function(tx) {
			tx.executeSql("DELETE FROM usuarios WHERE userId=?",[userId], listUsers);
		})
	}
</script>
</head>
<body onload="init()">
<div id="usersDiv">
	Usuarios en local
</div>
<div id="newuser">
	Crear nuevo usuario<br/>
	Usuario: <input type="text" id="username"/><br/>
	Password: <input type="password" id="password"/><br/>
	<button onclick="addUser(username.value, password.value)">Add User</button>
</div>
</body>

</html>

Desde luego es una gozada que sea tan sencillo trabajar con una base de datos de una forma tan sencilla :)

Además, tenemos que sumarle la gran calidad de las herramientas de depuración que nos aportan los navegadores, en la captura que encabeza la entrada podemos ver la que tiene Safari. Si somos usuarios de chrome veremos que la ventana es idéntica, esto es por que Chrome al igual que Safari se basa en webkit.

Después de leer el post, podemos sacar la conclusión de que es muy sencillo operar con bases de datos en cliente, y es totalmente cierto, pero ahora hay que pensar bien cuando esta opción es la mejor y cuando los datos solo se guardan en el servidor.


DSL en Ruby. Generador de LaTeX.

octubre 3, 2010

Lo cierto es que, Ruby es un lenguaje que cuanto más aprendes, más te sorprende. Lo último que he visto sobre el fantástico lenguaje creado por Matz, es la facilidad con que se pueden fabricar “interpretes” de lenguajes específicos, o lo que también se conoce como DSL por ser las anglosajonas siglas de Domain-specific Languages.

Como se puede esperar, estos lenguajes se centran en resolver problemas muy específicos, en lugar de, como C o Java, estar enfocados a un ámbito genérico. Ejemplos de estos DSL pueden ser lenguajes como SQL, HTML, VHDL o YACC. Cada uno de ellos existen para resolver un problema, y difícilmente se podrá utilizar para otra cosa. No me imagino hacer una web con YACC o una consulta a una base de datos con VHDL :-S

Crear estos lenguajes suele ser una tarea compleja, y en muchas ocasiones se suele usar otro lenguaje mas genérico. De hecho, hacer un “interprete” de un lenguaje usando C++ mediante macros y templates es un entretenimiento que no esta accesible a cualquiera ;)

Sin embargo, cuando Ruby se nos cruza en el camino, todo parece resolverse como por arte de magia.

Antes de meternos de lleno en el objetivo del post, que es una especie de sencillo interprete de LaTeX (si, el lenguaje que describe como es un documento :D ), vamos a ver que es lo que Ruby hace por que esto sea posible.

Sin duda, el principal elemento que añade la mayor parte de la magia es una sola sentencia, dicha sentencia es: “alias method_missing tag“.

Esa sentencia hará que cualquier invocación de un método no existente de una clase, se “reenviará” al método que le decimos a continuación, en nuestro caso, “tag”.

Si nos paramos a pensar la potencia de este mecanismo es tremenda, podemos hacer que nuestra clase “conteste” potencialmente a cualquier método. Sin duda le añade un factor dinámico al código que es difícil de encontrar en otros lenguajes, y sobre todo, con esa elegancia.

Otro elemento muy importante de la creación de DSL es el método instance_eval de cualquier clase de Ruby. Dicho método ejecutará lo que le pasemos como parámetro en el contexto del objeto sobre el que se invoca. La verdad es que es mas difícil explicarlo que verlo en acción, así que seguro que queda mas claro en este ejemplo:

class Prueba
  def metodo
    puts "Hola Mundo"
  end
end

p = Prueba.new
p.instance_eval "metodo"
=> "Hola Mundo"

Como veis, lo que hace instance_eval es ejecutar el parámetro, como si fuera código que se ejecuta sobre la variable p.

El último elemento clave son los bloques en Ruby. Sin duda uno de mis elementos favoritos del lenguaje, todo método en Ruby, puede llevar asociado un bloque, y dicho bloque se puede capturar como parámetro de un metodo. Si combinamos los bloques con el instance_eval, podemos tener lo siguiente.

class Prueba
  def metodo1
    puts "Método 1"
  end

  def metodo2
    puts "Método 2"
  end

  def self.generate(&block)
    Prueba.new.instance_eval(&block)
  end
end

Prueba.generate do
  metodo1
  metodo2
end
=> Método 1
=> Método 2

Mola, ¿no? :)

Pues bien, básicamente con estos elementos, podemos empezar nuestro mini-generador de LaTeX.

El objetivo será que usando una sintaxis mas próxima a Ruby, seamos capaces de crear un documento de LaTeX sin usar casi ninguna {} y sin preocuparnos de que ponemos un \end cuando debemos.

Para ello, debemos crear una clase que sea capaz de que ejecutar el siguiente código en un interprete de Ruby tenga como salida un documento con formato LaTeX

LaTeX.generate(STDOUT) do
  documentclass "article"
  usepackage "amsmath"
  title "\\LaTeX"
  date ""
  start "document" do
    title "hola"
    "Hola, hoy son las #{Time.now} y \\sqrt{2} = #{Math.sqrt(2)}"
  end
end

Como veis, ademas de tener una sintaxis mucho mas “limpia” ganamos la incorporación del propio código Ruby, pudiendo usar cosas como “Time.now” o operaciones como la raíz cuadrada.

Aunque parezca mentira, usando los mecanismos que hemos puesto anteriormente, la clase necesaria seria algo como:

class LaTeX
  def initialize(out)
    @out = out
  end
  
  def tag(tagname, name=nil)
    if tagname == :start
      tagname = :begin
    end
    @out << "\\#{tagname}"
    if name
      @out << "\{#{name}\}"
    end
    @out << "\n"
    if block_given?
      content = yield
      if content
        @out << content.to_s << "\n"
      end
      @out << "\\end"
      if name 
        @out << "\{#{name}\}"
      end
      @out << "\n"
    end
    nil
  end
    
  alias method_missing tag
  
  def self.generate(out, &block)
    LaTeX.new(out).instance_eval(&block)
  end
end

Unicamente, con ese fragmento sencillo de código, seremos capaces de que construir un documento LaTeX sea mucho mas sencillo que lo habitual, pues la ejecución tendrá como resultado

\documentclass{article}
\usepackage{amsmath}
\title{\LaTeX}
\date{}
\begin{document}
\title{hola}
Hola, hoy son las Sat Oct 02 23:49:35 +0200 2010 y \sqrt{2} = 1.4142135623731
\end{document}


Categoría para traducir entidades html en NSString

septiembre 20, 2010

Recientemente desarrollando para el iOS, me encontré con la necesidad de eliminar las entidades html de una cadena de texto.

Para el que no lo sepa, las entidades dentro de HTML, o en cualquier lenguage de marcas, son lo mas parecido a una secuencia de escape, es decir, una cadena que representa a un caracter que originalmente no esta permitido. Por ejemplo, y como parece obvio, si queremos poner el caracter ‘<’ en HTML, no podemos ponerlo directamente, pues forma parte del propio lenguage. Por ello, si queremos escribir dicho carácter tendremos que poner <.

La principal característica de las entidades de un lenguage de marcas, es que comienzan con ‘&’ y acaban con ‘;’.

Dentro de la API de manejo de cadenas de UIKit, existe un método para codificar URLs, que aunque pueda parecer similar, no lo es. Pues las URLs no tienen nada que ver con el lenguage HTML, solo nos indican la forma de acceder a los documentos que estan hechos con HTML ;)

Dichos métodos son, stringByAddingPercentEscapesUsingEncoding: y stringByReplacingPercentEscapesUsingEncoding:, y sustituyen los caracteres especiales de las URLs por sus equivalentes “escapados” usando porcentajes, como por ejemplo el símbolo del $ la convertirá en %24.

Pero sin embargo no hay ninguna función para transformar las entidades en sus caracteres equivalentes.

La primera solución que se nos puede ocurrir es usar el mensaje stringByReplacingOccurrencesOfString:withString: por cada entidad. Y aunque funciona, es bastante penalizador desde el punto de vista del rendimiento, ya que si tenemos el siguiente código.

NSString* cad = [NSString stringWithString:@"&lt;&apos;&amp;&apos;&gt;"];
	NSLog(@"%@", [[[[cad stringByReplacingOccurrencesOfString:@"&lt;" withString:@"<"]
				  stringByReplacingOccurrencesOfString:@"&gt;" withString:@">"]
				  stringByReplacingOccurrencesOfString:@"&amp;" withString:@"&"]
				  stringByReplacingOccurrencesOfString:@"&apos;" withString:@"'"]);

Recorrerá la cadena entera tantas veces como entidades queramos sustituir, lo que para cadenas largas puede ser un gasto bastante grande e innecesario.

Después de buscar por internet, no encontré nada que me convenciera, asi que tome el camino del medio, y hice una categoria de NSString, que recorre la cadena y sustituye las ocurrencias, de las entidades por sus caracteres.

Al ser una categoria, no hace falta mas que incluir el fichero .h y llamarlo sobre cualquier objeto NSString.

@interface NSString (RemoveHtmlEntities)

- (NSString*) stringByRemovingHtmlEntities;

@end

@implementation NSString (RemoveHtmlEntities)

- (NSString*) stringByRemovingHtmlEntities {
	NSMutableString* newStr = [[NSMutableString alloc] init];
	NSUInteger i = 0;
	unichar c = 0;
	NSUInteger strLen = [self length];
	while (i < strLen) {
		c = [self characterAtIndex:i];
		if ( c == '&' ) {
			if ( [self rangeOfString:@"amp;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (5>(strLen-i))?strLen-i:5)].location != NSNotFound ) {
				[newStr appendFormat:@"&"];
				i += 5;
			} else if ([self rangeOfString:@"lt;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (4>(strLen-i))?strLen-i:4)].location != NSNotFound) {
				[newStr appendFormat:@"<"];
				i += 4;
			} else if ([self rangeOfString:@"gt;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (4>(strLen-i))?strLen-i:4)].location != NSNotFound) {
				[newStr appendFormat:@">"];
				i += 4;
			} else if ([self rangeOfString:@"quot;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (6>(strLen-i))?strLen-i:6)].location != NSNotFound) {
				[newStr appendFormat:@"\""];
				i += 6;
			} else if ([self rangeOfString:@"apos;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (6>(strLen-i))?strLen-i:6)].location != NSNotFound) {
				[newStr appendFormat:@"'"];
				i += 6;
			} else {
				[newStr appendFormat:@"&"];
				i++;
			}
		} else {
			[newStr appendFormat:@"%c", c];
			i++;
		}
	}
	return [newStr autorelease];
}

@end

Como veis, solo transforma las entidades que necesitaba en el momento de desarrollo, pero es trivial añadir mas clausulas “else if” al código ;)

Un posible uso del código sería

	NSString* cad = [NSString stringWithString:@"&lt;&apos;&amp;&apos;&gt;"];
        NSLog(@"%@", [cad stringByRemovingHtmlEntities]); 
       // Imprime <'&'>


Juego de la Vida en un canvas de HTML5

septiembre 13, 2010

Poco a poco HTML5 va tomando forma, lo que hace un tiempo era poco mas que un proyecto de estandar, hoy en día ya es una realidad en la mayoría de los navegadores.

Si usamos Safari, Firefox o Chrome ya somos capaces de disfrutar de la mayoría de las ventajas que el nuevo estandar nos propone.

Una de las grandes mejoras es la habilidad para ponder “pintar” en una zona especialmente diseñada para ello. Dicha zona se conoce como “canvas” y vamos a poder utilizarla como si de cualquier otro contexto gráfico se tratara.

Seguramente pensareis que esto no es tan gran novedad, esto ya se podia hacer antes con cosas como Flash o Applets de Java, pero el canvas de HTML5 va un paso mas adelante.

Sin duda la principal novedad de dicho canvas no es tanto el que nos permita dibujar en el navegador sino que es que es propio navegador quien decide como tratar esa zona. A diferencia de la aproximación de flash o java donde era necesario un plugin en comunicación con el navegador.

Este cambio supone un salto drástico en el rendimiento de este tipo de aplicaciones. Seguro que si habéis usado flash o applets java os habréis dado cuenta lo exigente que era para las cosas que mostraban por pantalla. Sin embargo, con el canvas y una buena implementación por parte del navegador, no solo este rendimiento será mejor, sino que además podremos usar cosas como la acceleración nativa 3D.

Además, la ausencia de la necesidad de plugins facilita enormemente la labor de porting a otras plataformas, si el navegador soporta HTML5, no se necesitará nada mas.

Y para terminar con las ventajas, estamos hablando de un estándar, libre de patentes, no controlado por ninguna empresa y con los requerimientos totalmente abiertos. Cualquiera puede implementar un navegador HTML5 sin tener que pagar nada a nadie.

Se me nota entusiasmado, ¿no? :) . Creo que es para estarlo, pues la web es uno de los medios de comunicacion mas importante hoy en día, y el hecho de estandarizar algo tan común en la web como son estas aplicaciones es para estar de enhorabuena.

Pues bien, investigando un poco en como se usa el canvas de HTML5, me vino a la mente lo fácil que sería hacer un simulador del famoso “juego” “Juego de la Vida” de Conway.

El juego de la vida, es como se define en la wikipedia, un juego de 0 jugadores. ¿Un Juego sin jugadores? :) , básicamente diseñamos una situación inicial y lo único que hacemos es ver como evoluciona la población de células que forman el juego.

El juego está formado por un conjunto de células, representadas por un punto, si la célula esta viva, se pintará de color verde, y si la célula esta muerta, no se pintará. Según ciertas condiciones dependientes del numero de vecinos, cada célula vivirá, morirá o nacerá en la siguiente generación. La implementación es muy sencilla y al igual que con la entrada anterior sobre Mandelbrot, los resultados son bastante interesantes y sin duda algunos patrones de evolución son bastante vistosos.

Aconsejo echar un vistazo a la entrada de la wikipedia para saber un poco más en que consiste el juego.

Hablando de la implementación, pintar en el canvas de HTML5 es bastante sencillo, básicamente, se reduce a 3 lineas

// ...
var context = canvas.getContext("2d");
context.fillStyle = "rgb(100,0,100)";
context.fillRect(10,10, 100, 100);
// ...

Con esas 3 líneas dibujamos un cuadrado en la posicion 10, 10 del canvas. De hecho una vez que obtenemos el contexto, podemos hacer las típicas operaciones, como dibujar cuadrados, arcos, rutas o imagenes.

Para mas detalles, os aconsejo que visiteis el sitio de desarrolladores de mozilla, donde tienen un fantastico tutorial de lo que se puede hacer con el canvas: https://developer.mozilla.org/en/Canvas_tutorial

Finalmente, os dejo con el código fuente de una implementación sencilla del juego de la vida y que como no puedo incrustar en este post de wordpress, podeis ver aquí

<html>
<head>
	<script type="text/javascript">
		var cellSize=10;
		var hcells = 70;
		var vcells = 40;
		var board;
		var canvas;
		var context; 
		var timerId = undefined;
		
		function drawShapeInBoard()
		{
			// Draw Some shapes in the board
			
			board[10][10] = board[12][10] = board[12][9] = board[14][8] = board[14][7] = board[14][6] = board[16][7] = board[16][6] = board[16][5] = board[16][6] = 1;
			
			board[25][30] = board[26][30] = board[27][30]  = board[28][30] = board[29][30] = board[29][30] = board[30][30] = board[31][30] = board[33][30] = board[34][30] = board[35][30] = board[29][30] = 1;
		}
		
		function initGame() {
			canvas = document.getElementById("gameoflife");
			context = canvas.getContext("2d");		
			canvas.addEventListener("click", onCanvasClick, false);	
			initBoard();
			drawShapeInBoard();
			drawBoard();
		}
		
		function onMouseMove(evt) {
			context.fillStyle = "rgb(200,200,200)"			
			context.fillRect(evt.clientX - canvas.offsetLeft, evt.clientY - canvas.offsetTop, cellSize, cellSize);
		}
		
		function onCanvasClick(evt) {
			var x = evt.clientX - canvas.offsetLeft;
			var y = evt.clientY - canvas.offsetTop;
			var boardX = parseInt(x / cellSize);
			var boardY = parseInt(y / cellSize);
			if ( isAlive(boardX, boardY) ) board[boardX][boardY] = 0
			else board[boardX][boardY] = 1;
			
			drawBoard();
		}
		
		function initBoard() {
			board = [];
			for ( i = 0; i < hcells; i++) {
				board[i] = [];
				for ( j = 0; j < vcells; j++ ) {
					board[i][j] = 0;
				}
			}
		}
		
		function drawBoard() {
			for ( i = 0; i < hcells; i++) {
				for ( j = 0; j < vcells; j++ ) {
					if ( board[i][j] == 0){
						context.fillStyle = "rgb(180,180,180)";
					} else {
						context.fillStyle = "rgb(0,170,0)";
					}					
					context.fillRect(i*cellSize, j*cellSize, cellSize, cellSize);					
				}
			}
		}
		
		function isAlive(x,y) {
			if ( board[x] ) 
				if ( board[x][y] == 1) return true;
			return false;
		}
		
		function numberOfNeighbours(x, y) {
			var count = 0;
			if ( isAlive(x-1, y   ) ) count++;
			if ( isAlive(x-1, y-1 ) ) count++;
			if ( isAlive(x-1, y+1 ) ) count++;
			if ( isAlive(x  , y-1 ) ) count++;
			if ( isAlive(x  , y+1 ) ) count++;
			if ( isAlive(x+1, y-1 ) ) count++;
			if ( isAlive(x+1, y+1 ) ) count++;
			if ( isAlive(x+1, y ) ) count++;
			
			return count;
		}
		
		function nextStep() {
			var newBoard = [];
			for ( i = 0; i < hcells; i++) {
				newBoard[i] = [];
				for ( j = 0; j < vcells; j++ ) {
					var neighbours = numberOfNeighbours(i, j);
					if ( isAlive(i,j) && neighbours < 2) newBoard[i][j] = 0;
					else if ( isAlive(i,j) && neighbours > 3 ) newBoard[i][j] = 0;
					else if ( isAlive(i,j) && (neighbours == 2 || neighbours == 3) ) newBoard[i][j] = 1;
					else if ( !isAlive(i,j) && (neighbours == 3) ) newBoard[i][j] = 1;
					else newBoard[i][j] = 0;
				}
			}
			board = newBoard;
		}
		
		function computeNS() {
			nextStep();
			drawBoard();
		}
		
		function startNS() {
			if ( ! timerId) {
				timerId = setInterval("computeNS()", 150);
				var status = document.getElementById("status");
				status.innerHTML = "<b>Running</b>";
			}
		}
		
		function stopNS() {
			clearInterval(timerId);
			timerId = undefined;
			var status = document.getElementById("status");
			status.innerHTML = "<b>Stopped</b>";
		}
		
		function clearBoard() {
			initBoard();
			drawBoard();
		}
		
	</script>
</head>
<body onload="initGame()">
	<p align="center">
		The Game of Life
	</p>
	<p align="center">
		<canvas id="gameoflife" width="700" height="400" border="1"></canvas>
		<br/>
		<button onclick="computeNS()">Next Step</button>
		<button onclick="startNS()">Start</button>
		<button onclick="stopNS()">Stop</button>
		<button onclick="clearBoard()">Clear</button>
		<br/>
		<span id="status"><b>Stopped</b></span>
	</p>
</body>	
</html>


Dibujando un Fractal Mandelbrot con Ruby

septiembre 6, 2010

Hace mucho tiempo, cuando aún me pegaba con pascal en primero de carrera empecé a descubrir los fractales. Aún recuerdo lo bien que lo pasaba con el fractint, “navegando por las profundidades de mandelbrot”. Investigando un poco mas, descubrí que la formula que se utilizaba para generar dicho fractal era bastante sencilla, aún así, un software como el fractint parecía demasiado complejo.

Sin embargo, la curiosidad me picaba, ¿Qué pasaría si hiciera un programa que siguiera la formula que sirve para generar el fractal de mandelbrot?. Al fin y al cabo, la formula parecía sencilla de programar incluso para un imberbe programador :)

Sin más espera me puse manos a la obra y con el amado Pascal, hice un programa que supuestamente dibujaría el fractal de mandelbrot. ¿Y qué pasó?, pues ante mi asombro, al ejecutar el programa, allí estaba el famoso gráfico con forma de guitarra. ( Notese la licencia literaria, como buen programador, nada funciona a la primera :D )

Hace poco, trasteando con el nuevo “juguete”, ruby, me volvio a picar la curiosidad de dibujar de nuevo el fractal. En esta ocasión, gracias a las utilidades que nos da un lenguaje como Ruby y una gran libreria como SDL, en pocos minutos tenia el famoso gráfico en pantalla.

Centrandonos en el lado técnico del fractal, dibujar el conjunto de mandelbrot es más o menos sencillo.

Para cada numero imaginario se desarrolla la serie z(n+1) = z(n)^2 + c. Siendo la z(0) = 0,0 y c el punto sobre el que calculamos el desarrollo de la serie. Para algunos valores de c, la serie converge, y para otros rapidamente escapa a un valor umbral que habitualmente se establece en 2+2i. Si la serie converge, el punto no pertenece a la serie de mandelbrot, y por lo tanto se pinta de negro. Para los puntos que escapan al valor umbral, según los pasos que han necesitado para escapar se les pinta de un color. Pero eso si, a todos los que escapan con el mismo numero de pasos, se les da el mismo color.

Así por ejemplo, para el punto 1+i, si desarrollamos la serie tendremos:

z(1) = (0+0i)^2 + 1+i => 1+i
z(2) = (1+i)^2 + 1+i => 1+3i
z(3) = (1+3i)^2 + 1+i => -7+7i

z(3) escapa de los límites, por lo que el punto 1+i, pertenece al conjunto de mandelbrot y se pintará con el color elegido para los números que escapan en 3 pasos.

Si hacemos esto mismo con cada punto dentro del rango 2, -2 tanto para real como para imaginario, obtendremos el gráfico que encabeza el post.

Aunque han pasado unos cuantos años, no deja de sorprenderme que un programa tan sencillo tenga como salida un gráfico tan interesante y, por que no, bonito.

Realmente el fractal mandrelbrot es apasionante, una de sus propiedades es debido a que es un fractal, se autoreplica, por lo que podemos encontrar la misma forma si vamos haciendo zoom dentro del gráfico. No voy a entrar en mas detalles, pero os dejo con el enlace de la wikipedia que describe muchos detalles de este fascinante mundo.

Para finalizar el post, os dejo con el código ruby que dibuja la imagen. Lo único que necesitais para ejecutarlo es ruby y rubysdl.

require 'sdl'

WIDTH = 600
HEIGHT = 400
REALRANGE = 2
IMRANGE = 2
MAXSTEPS = 20

def mandelbrot_steps(x, y)
	steps = 0
	newx = newy = 0
	while steps < MAXSTEPS and ( ((newx*newx) + (newy*newy)) < ((REALRANGE*REALRANGE) + (IMRANGE*IMRANGE)) ) do
		xtemp = (newx * newx) - (newy * newy) + x
		newy = 2 * newx * newy + y
		newx = xtemp
		steps+=1
	end

	return steps
end

SDL.init SDL::INIT_VIDEO
screen = SDL.set_video_mode WIDTH, HEIGHT, 24, SDL::HWSURFACE

pallete = Array.new
pallete[0] = screen.format.mapRGB(0,0,0)
pallete[1] = screen.format.mapRGB(40,36,196)
pallete[2] = screen.format.mapRGB(30,144,255)
pallete[3] = screen.format.mapRGB(0,0,128)
pallete[4] = screen.format.mapRGB(0,255,0)
pallete[5] = screen.format.mapRGB(0,255,255)
pallete[6] = screen.format.mapRGB(0,0,255)
pallete[7] = screen.format.mapRGB(255,255,255)
pallete[8] = screen.format.mapRGB(255,100,255)
pallete[9] = screen.format.mapRGB(0,255,255)
pallete[10] = screen.format.mapRGB(100,255,100)

(-WIDTH/2).upto WIDTH/2 do |x|
	(-HEIGHT/2).upto HEIGHT/2 do |y|
		scaledX = (x * 2)/(WIDTH/2).to_f - 0.7
		scaledY = (y * 2)/(HEIGHT/2).to_f
		color = pallete[0]
		steps = mandelbrot_steps scaledX, scaledY
		color =  pallete[steps % 10] if steps < MAXSTEPS
		screen.put_pixel x+(WIDTH/2), y+(HEIGHT/2), color
	end
end

running = true
while running do
	while event = SDL::Event2.poll do
		case event
		when SDL::Event2::Quit
			running = false
		end
	end

	screen.flip
end

SDL.quit


RubyQuiz #60. Numeric Maze

agosto 1, 2010

En la época del año en la que estamos lo habitual es que o bien las vacaciones estén cerca o incluso ya estemos disfrutando de ellas. Sin duda es un buen momento para desconectar y hacer cosas que durante el año no podemos hacer.

A los que nos gustan los ordenadores, esto no tiene por que significar que dejemos de usarlos, simplemente podemos usarlos para otras cosas ;)

Personalmente me encantan los problemas numéricos y para ello el ordenador es una herramienta perfecta para desarrollar simples algoritmos que los solucionen.

Hace poco descubrí el rubyquiz . En esta web y en la lista de correo se mandan regularmente diversos problemas y se propone el desarrollar un pequeño programa hecho en Ruby que lo solucione.

El primer reto que he escogido es el número 60, llamado Numeric Maze.

El objetivo es hacer un programa que te calcule la ruta desde un número inicial A a un numero final B, con solo 3 operaciones: sumar 2, dividir por 2 y multiplicar por 2.

Así por ejemplo para llegar de 2 a 9, la ruta seguida será. [2, 1, 3, 5, 7, 9]

Como podemos ver, una de las formas de resolver el problema se trata de construir un con las diferentes posibilidades. A priori lo podemos plantear como un caso de backtracking, o de exploración del árbol en profundidad, sin embargo, esta forma de intentar solucionarlo tiene un problema. No sabemos cuando parar descendiendo por el árbol de soluciones.

La alternativa es explorar el árbol de forma horizontal, es decir, para cada paso, sacar las posibles posibilidades y ir generando el árbol paso a paso con todos los casos en cada nivel. De esta manera nos aseguramos que encontraremos la solución tan pronto aparezca. Aunque la desventaja general de este tipo de métodos es que se generan pasos que no van a ser utilizados para nada.

Además, se plantea una interesante posibilidad de “poda” del árbol, pues no es necesario volver a explorar un número que ya se ha explorado.

De esta manera, el árbol ejemplo para el caso de 2 a 9 seria:

[2]
[4 1]
[8 6 3]
[16 10 12 5]
[18 32 20 24 14 7]
[36 9 64 34 22 40 ...]

Para representar este árbol en Ruby, he escogido una lista que esta compuesta por cada numero del nivel determinado. Como tenemos que guardar la ruta de números que nos ha llevado hasta el resultado, acompañando a cada número se guarda dicho histórico.

Así en el ejemplo anterior, la estructura de datos que manejamos es

[2 []]
[ [4, [2]], [1, [2]] ]
[ [8, [4, 2]], [6, [4, 2]], [3, [1,2]] ]
...
[... [9 [2, 4, 8, 16, 18, 9]],...]

De esta manera cuando lleguemos a la solución ya tenemos el conjunto de números por los que hemos ido pasando.

Finalmente el código Ruby que resuelve el problema es el siguiente:

def solve startNumber, endNumber
  current = []
  curStep = 0
  current[curStep] = [ [startNumber, []] ]
  numbers = [startNumber]
	while (numbers.index endNumber) == nil
	  curStep+=1
	  current[curStep] = []
    current[curStep-1].each do |tuple|
	    if (numbers.index tuple[0] << 1) == nil
	      current[curStep] << [(tuple[0] << 1), tuple[1] + [tuple[0]]]
	      numbers << (tuple[0] << 1)
      end

	    if ((tuple[0] % 2) == 0) and ((numbers.index(tuple[0] >> 1) ) == nil)
	      current[curStep] << [(tuple[0] >> 1), tuple[1] + [tuple[0]]]
	      numbers << (tuple[0] << 1)
      end

	    if (numbers.index tuple[0]+2) == nil
	      current[curStep] << [tuple[0]+2, tuple[1] + [tuple[0]]]
	      numbers << tuple[0]+2
      end
    end
  end

  current[curStep].each do |res|
    return res[1] + [endNumber], curStep if res[0] == endNumber
  end

  return nil
end

p solve 2, 9

# [[2, 4, 8, 16, 18, 9], 5]
# Siendo 5 el numero de pasos usados para llegar a la solución

Como veis el código, aunque en esta Ruby, aun le queda para tener ese toque “perl-extraño” que suelen tener los scripts hechos en Ruby. Esto se debe a que aún mi nivel del lenguaje no es muy alto ;)


Métodos privados en Objetive-C

julio 25, 2010

Si programamos habitualmente en Objetive-C seguramente nos habremos encontrado con la necesidad de crear un método que sea solo accesible desde dentro de la clase, vamos, un método privado :)

Seguramente, en este momento recordemos como se hace en otros lenguajes, pero no podemos recordar como se hacia en este lenguaje. El motivo de esta falta de recuerdo es por que realmente no nos habremos encontrado una sección sobre ello en el libro o web que usamos para aprender este lenguaje.

Si bien existe la habituales directivas de ámbito @private, @public y @protected de compilador, solo tienen aplicación en los atributos de la clase, y no en los métodos. Además al igual que pasa con otros lenguajes estas directivas solo se aplican en el compilador, pero en el caso de Objetive-C, si accedemos a estas variables privadas, solo obtendremos un warning, como el que vemos en la imagen.

Pero entonces ¿como definimos métodos privados?. Antes de ello vamos a repasar que contiene un fichero de implementación.

Los ficheros .m habitualmente contienen la definición de los métodos que se han declarado en el fichero .h. Como seguramente sabremos, dichas definiciones están comprendidas entre las directivas @implementation CLASE y @end. ¿Que pasa si intentamos definir un método fuera de este bloque?. Pues depende ;) , si intentamos definir un método de la clase, el compilador no nos dejara, pero si por otro lado tratamos de definir una función de C o C++, el compilador no pondrá ningún problema. De esta manera podemos pensar que la sección entre @implementation y @end es la parte Objetive-C del fichero .m. Fuera de esta sección no podremos acceder a cosas como “self”, aunque si podremos meter sin problemas código en Objetive-C. Por ejemplo, veamos el contenido del siguiente fichero .m.

int sumaC(int a, int b) {
	PruebaPrivado *pp = [[PruebaPrivado alloc] init];
	return [pp suma:a with:b];
}

@implementation PruebaPrivado
-(int) suma: (int) op1 with: (int) op2 {
	return op1 + op2;
}

-(int) suma2: (int) op1 with: (int) op2 {
	return sumaC(op1, op2);
}

@end

En Objetive-C se sigue una filosofía parecida. La primera aproximación de método privado pasa por declarar esa función únicamente en el fichero .m, y no en el .h. De esta manera los clientes de la clase importarán en fichero .h, y no sabrán de la existencia de los métodos privados. Eso si, como hemos visto, si queremos acceder al contenido de la clase, debe estar definida dentro del bloque de implementación. Como podemos ver en el siguiente ejemplo.

@implementation PruebaPrivado

-(int) suma: (int) op1 with: (int) op2 {
	return [self sumaPrivada: op1 with: op2];
}

-(int) sumaPrivada: (int)o1 with: (int) o2 {
	return o1 + o2;
}

@end

Sin embargo, esta forma de hacerlo tiene sus inconvenientes, por un lado, si compilamos el código del ejemplo el compilador nos dará un warning. Este warning viene de que sumaPrivada no esta definido en el momento de compilar el método suma. Por lo que el compilador nos informa de que PruebaPrivado puede no responder al mensaje sumaPrivada. Este warning se solucionaría poniendo la declaración del método antes de su uso, como pasa habitualmente con C.

Pero aún sin el warning, otro principal inconveniente es la legibilidad. Si intercalamos métodos privados con públicos, mas adelante puede ser un follón revisar nuestro propio código, ya que no tendremos agrupadas todas las funciones privadas de nuestra clase.

Aunque las solución anterior presentaba ciertos inconvenientes, sienta las bases de la solución que habitualmente se suele usar. Con la incorporación de las categorías en Objetive-C se abre la posibilidad de modificar una clase “a posteriori” como ya vimos en esta otra entrada.

La solución pasa por crear una categoría en el fichero .m de implementación, que no estará visible por ningún cliente y que agrupara los métodos privados, de esta manera todos los métodos estarán declarados en el mismo lugar, y nos quitaremos tanto el problema del warning del compilador como la desorganización.

Por tanto, el código quedara como:

@interface PruebaPrivado ()

-(int) sumaPrivada:(int)o1 with:(int)o2;

@end


@implementation PruebaPrivado

-(int) suma: (int) op1 with: (int) op2 {
	return [self sumaPrivada: op1 with: op2];
}

-(int) sumaPrivada: (int)o1 with: (int) o2 {
	return o1 + o2;
}

@end

Como vemos, ahora sumaPrivada esta declarada dentro de la categoría, que además como habéis visto, no lleva nombre. Este hecho nos aporta alguna ventaja como es que la definición de los métodos puede ir en la misma sección de implementación que los métodos públicos, en lugar de tener que crear una sección especifica para esta categoría.


Seguir

Get every new post delivered to your Inbox.