Ciao a tutti, come promesso sul Forum oggi vi faccio vedere come utilizzare una classe esterna per passare oggetti tra viste. In particolare io la utilizzo sempre per creare le detailView delle UITableView.
L’idea di fondo è questa: realizzare una classe in cui sono presenti ufficialmente due metodi:
- impostaValore
- ottieniValore
La nostra classe verrà inizializzata la prima volta che la utilizziamo e poi basta, così da poterci accedere.
Iniziamo con il nostro progetto
Creaiamo un progetto “Navigation-Based”, che includerà già una tabella, e nel file .h definiamo un array e un viewController (ci servirà per passare alla vista dettagli)_
#import
@interface RootViewController : UITableViewController
{
NSArray * arrayValori;
UIViewController * temp;
}
@end
A questo punto passiamo al metodo “viewDidLoad” del file di implementazione .m e inizializziamo l’array:
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"Singleton";
NSDictionary * dict1 = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"iPhone 3G", @"Questo è un iPhone 3G", nil]
forKeys:[NSArray arrayWithObjects:@"titolo", @"descrizione", nil]];
NSDictionary * dict2 = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"iPhone 3GS", @"Questo è un iPhone 3GS", nil]
forKeys:[NSArray arrayWithObjects:@"titolo", @"descrizione", nil]];
NSDictionary * dict3 = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"iPhone 4", @"Questo è un iPhone 4", nil]
forKeys:[NSArray arrayWithObjects:@"titolo", @"descrizione", nil]];
NSDictionary * dict4 = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"iPhone 5", @"Questo modello non esiste", nil]
forKeys:[NSArray arrayWithObjects:@"titolo", @"descrizione", nil]];
arrayValori = [[NSArray alloc] initWithObjects:dict1, dict2, dict3, dict4, nil];
}
Su ogni dizionario abbiamo inserito un valore per “titolo” e uno per “descrizione” che visualizzeremo nella “detailView”.
Modifichiamo ora il metono “numberOfRowInSection” come segue:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [arrayValori count];
}
e nel metodo:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
inseriamo prima del “returm cell;” la seguente istruzione:
cell.textLabel.text = [[arrayValori objectAtIndex:indexPath.row] objectForKey:@"titolo"];
Proviamo ad eseguire sul simulatore. Dovremmo ottenere una tabella come questa:

La classe “singleton”
Andiamo ora su File -> New File e selezioniamo “Objective-c class”. Verifichiamo quindi che la classe erediti da “NSObject” (altrimenti impostiamolo noi a mano)
Chiamiamo questa classe “singleton”.
Ecco cosa dovremo inserire nel nuovo file .h:
@interface singleton : NSObject {
}
+ (void)impostaArray:(NSMutableArray *)valore;
+ (NSMutableArray *)ritornaArray;
+ (void)impostaItem:(NSMutableDictionary *)valore;
+ (NSMutableDictionary *)ritornaItem;
+ (void)impostaStringa:(NSMutableString *)valore;
+ (NSMutableString *)ritornaStringa;
@end
Abbiamo inserito 6 metodi, ogni coppia ci permetterà di settare e ottenere array, dictionary e stringhe.
Passiamo al file .m e definiamo quindi i metodi appena dichiarati:
NSMutableArray *arrayCondiviso;
NSMutableDictionary *itemCondiviso;
NSMutableString * stringaCondivisa;
static singleton *sharedClass = nil;
// Con questo metodo imposto il valore dell'item condiviso
+ (void)impostaArray:(NSMutableArray *)valore{
arrayCondiviso = valore;
}
// Con questo metodo, prelevo il valore dell'item condiviso da qualsiasi classe
+ (NSMutableArray *)ritornaArray{
return arrayCondiviso;
}
+ (void)impostaItem:(NSMutableDictionary *)valore{
itemCondiviso = valore;
}
+ (NSMutableDictionary *)ritornaItem{
return itemCondiviso;
}
+ (void)impostaStringa:(NSMutableString *)valore{
stringaCondivisa = valore;
}
// Con questo metodo, prelevo il valore dell'item condiviso da qualsiasi classe
+ (NSMutableString *)ritornaStringa{
return stringaCondivisa;
}
+ (singleton *)sharedManager
{
@synchronized(self) {
if (sharedClass == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedClass;
}
+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self) {
if (sharedClass == nil) {
sharedClass = [super allocWithZone:zone];
return sharedClass; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
Non preoccupatevi se non capite tutto il codice, vi basta sapere che la prima volta che utilizzate questa classe lei chiama il metodo “init” e vi permette di utilizzarla per impostare un oggetto.
La classe “detailView”
Fatto questo procediamo nel creare la vista dettaglio. Andate in File -> New File e creiamo una classe di tipo “UIViewController subclass” con rispettivo file .xib (ma che non erediti da UITableView) e chiamiamola “detailView”.
Ritorniamo un attimo al nostro RootViewController e nel metodo:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Inseriamo il seguente codice:
[singleton impostaItem:[arrayValori objectAtIndex:indexPath.row]];
temp = [[detailView alloc] initWithNibName:@"detailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:temp animated:YES];
[temp release];
temp = nil;
Ricordiamoci di importare nel “RootViewController.h” sia la classe “singleton.h” che la “detailView.h”
Passiamo alla classe detailView.h:
#import
#import "singleton.h"
@interface detailView : UIViewController {
UILabel * descrizione;
}
@end
Anche qui non abbiamo fatto altro che importare di nuovo la classe singleton e dichiarare un UILabel che conterra la nostra descrizione.
Mentre nel file .m allochiamo semplicemente la label e ne impostiamo il testo ottenuto dalla classe singleton in questo modo:
- (void)viewDidLoad
{
[super viewDidLoad];
descrizione = [[UILabel alloc] initWithFrame:CGRectMake(50, 200, 300, 20)];
descrizione.text = [[singleton ritornaItem] objectForKey:@"descrizione"];
[self.view addSubview:descrizione];
}
Ecco qui il nostro progetto con il passaggio di variabili.. quello che dovete fare è semplicemente importare la classe, impostare il valore e poi ottenerlo.
Ogni oggetto resta impostato finchè non lo sovrascrivete, quindi potete anche richiamare piu volte
[singleton ritornaItem]
se non l’avete modificata prima.
Ciao, alla prossima
Se avete problemi con il tutorial, questo è il nostro file di progetto.









20 Responses to “T#076 – Utilizziamo una classe esterna per passare oggetti tra viste differenti”
18 Ottobre 2010
Tweets that mention Utilizziamo una classe esterna per passare oggetti tra viste differenti | devAPP -- Topsy.com[…] This post was mentioned on Twitter by raaden, Rynox and iPadWorld.it, devAPP. devAPP said: Utilizziamo una classe esterna per passare oggetti tra viste differenti http://bit.ly/agxwCa […]
18 Ottobre 2010
stefanoCiao Andrea e grazie per il tutorial e la promessa mantenuta!
Questo metodo è applicabile anche se l’array viene fatto dal parsing di un xml?
Grazie!
18 Ottobre 2010
Andrea Cappellottocerto, a qualsiasi tipo di array, che contiene qualsiasi cosa, e se utilizzi impostaItem e impostaStringa puoi utilizzare la classe anche per dizionari e stringhe
18 Ottobre 2010
GianlucaIn questo caso come si divide la tabella in sezioni? Ho provato ma al più sono riuscito a ottenere una tableview con le sezioni giuste ma che mostrava tutti gli elementi XD. Lo so che deve essere una ca***ta, non ci far caso, rispondimi semplicemente XD.
18 Ottobre 2010
Andrea CappellottoCiao, per questa cosa ci conviene andare avanti nel forum, non è cosi facile come si pensa..:)
18 Ottobre 2010
GianlucaOk, ho appena postato una risposta al tread del tutorial 😉
18 Ottobre 2010
stefanoallora, seguendo i tutorial del forum avevo già parsato il mio xml… il metodo era tutto nella classe controller del del mio tableview controller.. adesso devo importare tutto nella mia nuova singleton giusto? in modo che li venga inizializzato e popolato il mio array… praticamente dovrei prendere le stesse cose e spostarle?
e poi potrei riutilizzare l’array nella vista di dettaglio?
18 Ottobre 2010
stefanoPerfetto! Funziona tutto! Sono riuscito a implementare nel parser del mio array! Grande!
19 Ottobre 2010
andreaperfetto:) mi fa piacere..:)
19 Ottobre 2010
MacMomoUna domanda.
Ma in questo modo non c’è il rischio che gli oggetti puntati dalla classe singleton vengano rilasciati a nostra insaputa?
E che quindi puntino in realtà a zone ignote di memoria, causando dei possibili crash?
19 Ottobre 2010
andrearilasciati a nostra insaputa no, al massimo potresti perdere il dato se l’applicazione crasha inaspettatamente, ma durante un funzionamento normale puoi stare al sicuro
19 Ottobre 2010
MacMomoPuoi stare al sicuro nel senso che il programmatore dovrebbe sapere dove e come usa certi metodi, quindi dovrebbe evitare di rilasciare nel frattempo gli oggetti assegnati tramite la classe singleton, ma se un’app è molto grande e complicata secondo me non è la soluzione più indicata. O almeno sarebbe da aggiungere un retain/release degli oggetti assegnati, per qualsiasi eventualità.
Nell’esempio indicato tra l’altro non era più semplice aggiungere un metodo alla classe detailView che facesse assegnare direttamente i valore del label?
19 Ottobre 2010
andreasinceramnte non lo utilizzerei neanke io per un app troppo complicata, ma per due viste e per iniziare direi che è la cosa migliore, sopratutto per chi usa ancora interface builder… questo è solo un modo, aggiungere un metodo alla detailView è sicuramente la cosa migliore, ma nessuno vieta di fare così..
19 Ottobre 2010
antonionon vedo l’utilità della cosa. Se devo passare un oggetto ad una vista di dettaglio uso le proprietà e con poche righe di codice ho lo stesso risultato con un controllo maggiore della memoria ed elimino il rischio di concorrenza sui valori degli attributi dell’oggetto in questione.
9 Novembre 2010
ErrivaraCiao andrea, sei sparito dal mondo?
Il tuo blog non c’è più e ti ho pure inviato una mail a cui non rispondi;-)
Ti prego mandami una mail a enricovara@hotmail.it che ti devo parlare di un progetto:-)
15 Novembre 2010
MarcoScusate e se dovessi inserire un’immagine invece che una label in detail View come posso fare?
Grazie… 😀
18 Novembre 2010
CesareArticolo interessante e l’ho applicato ad una tableview con due viste ma capovolgendo la cosa-sono le viste che implementano una mutablearray comune e quindi i dati della tableview mapiccolo problema quando la tableview deve aggiornarsi perche la mutablearray passata a “singleton” e mutata, l’applicazione va in crash, applico il reloadData ma l’applicazione non accetta che si muti la mutableArray.
Avete una soluzione
Ciao a tutti
8 Settembre 2011
milonetveramente un’ottimo modo per passare i dati da una classe ad un’altra.. stavo giusto impazzendo in questi giorni per capire come si faceva! ottimo 😀
15 Ottobre 2012
Salvograzie Andrea,
funziona tutto, però ora con XCode 4.5 ottengo un warning per la memoria (Potential leak of an Object) in questo metodo:
—
+ (Singleton *)sharedManager
{
@synchronized(self) {
if (sharedClass == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedClass;
}
—
Come possiamo evitarlo?
grazie, ciao
salvo
16 Ottobre 2012
Salvook,
qui sembra funzionare (il warning scompare e non vedo per ora controindicazioni) cambiando in questo modo:
+ (Singleton *)sharedManager
{
@synchronized(self) {
if (sharedClass == nil) {
/// [[self alloc] init]; // assignment not done here
// per evitare il warning "Potential leak of an Object"
sharedClass = [[super allocWithZone:NULL] init];
}
}
return sharedClass;
}
che ne pensate?