Sto passando dalla gestione manuale della memoria ad ARC e ho un problema. La maggior parte delle volte eseguo il caricamento dei dati in modo asincrono chiamando performSelectorInBackground nelle mie classi modello. Il fatto è che ho bisogno di interrompere qualsiasi esecuzione del codice modello quando il modello riceve nil (rilascio). In non-arc, tutto era semplice: non appena un utente chiude la finestra, il suo controllore inizia a deallocarsi e rilascia il suo modello [_myModel release], e così il modello interrompe l'esecuzione del codice (caricamento dei dati) e viene chiamato il suo metodo dealloc .ARC: inviare nil a un oggetto non chiama immediatamente il suo dealloc
Questo sembra essere diverso in ARC. Il modello esegue ancora il codice anche dopo aver ricevuto il messaggio nullo dal controller. Il suo metodo dealloc viene chiamato solo dopo l'esecuzione del codice (caricamento dati). Questo è un problema perché l'esecuzione del codice dovrebbe fermarsi al più presto quando un utente chiude la finestra (controller). È una sorta di mancanza di controllo sul codice - il controller dice al modello - "vai via, non ho più bisogno del tuo lavoro" ma il modello continua a "funzionare per completare il suo lavoro" :).
Immaginate che un modello esegua un'elaborazione dei dati molto pesante con una durata di 10 secondi. Un modello inizia a eseguire l'elaborazione quando un utente apre la finestra (controller). Ma l'immagine di un utente cambia idea e chiude la finestra, subito dopo l'apertura. Il modello esegue ancora una lavorazione dispendiosa. Qualche idea su come risolvere o risolvere questo? Non mi piace l'idea di avere una proprietà BOOL "shouldDealloc" speciale nel mio modello e impostata su SÌ nel metodo dealloc del controller e utilizzare nelle condizioni della mia classe modello. C'è una soluzione più elegante?
Ho fatto qualche progetto dimostrativo per mostrare il problema. Per i test basta creare un'applicazione a vista singola e incollare il codice. Crea per Bottoni- "Start calcolare" e "Stop calcolare" nel file di ViewController.xib, e collegare le loro IBActions con startCalculationPressed e stopCalculationPressed:
ViewController.h
#import "MyModel.h"
@interface ViewController : UIViewController <MyModelDelegate>
- (IBAction)startCalculationPressed:(id)sender;
- (IBAction)stopCalculationPressed:(id)sender;
@end
ViewController.m
@interface ViewController(){
__strong MyModel *_myModel;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)didCalculated
{
NSLog(@"Did calculated...");
}
- (IBAction)startCalculationPressed:(id)sender
{
NSLog(@"Starting to calculate...");
_myModel = nil;
_myModel = [[MyModel alloc] init];
_myModel.delegate = self;
[_myModel calculate];
}
- (IBAction)stopCalculationPressed:(id)sender
{
NSLog(@"Stopping calculation...");
_myModel.delegate = nil;
_myModel = nil;
}
@end
Aggiungi nuova classe MyModel al progetto:
MyModel.h
@protocol MyModelDelegate <NSObject>
- (void)didCalculated;
@end
@interface MyModel : NSObject
@property (nonatomic, weak) id<MyModelDelegate> delegate;
- (void)calculate;
@end
MyModel.m
@implementation MyModel
- (void)dealloc
{
NSLog(@"MyModel dealloc...");
}
- (void)calculate
{
[self performSelectorInBackground:@selector(performCalculateAsync) withObject:nil];
}
- (void)performCalculateAsync
{
// Performing some longer running task
int i;
int limit = 1000000;
NSMutableArray *myList = [[NSMutableArray alloc] initWithCapacity:limit];
for (i = 0; i < limit; i++) {
[myList addObject:[NSString stringWithFormat:@"Object%d", i]];
}
[self performSelectorOnMainThread:@selector(calculateCallback) withObject:nil waitUntilDone:NO];
}
- (void)calculateCallback
{
[self.delegate didCalculated];
}
@end
UPDATE Martin è giusto, performSelectorOnMainThread mantiene sempre sé, quindi non c'è w come fermare l'esecuzione del codice su altri thread (sia in ARC che in non ARC), quindi dealloc non viene chiamato immediatamente al rilascio del modello. Quindi, dovrebbe essere fatto esplicitamente usando la proprietà appropriata (ad esempio delegato) con il controllo condizionale.
perché non limitarsi a tenere un bool controllato nel ciclo di esecuzione "performCalculateAsync' per terminare la funzione quando è impostato da" stopCalculationPressed "(avrà bisogno di una sorta di mutex prob) forse il tuo programma non ha un ciclo o da qualche parte è possibile inserire questo controllo in modo appropriato, ma se lo fa ... – Fonix
@Fonix Vedete è solo un semplice codice demo. Ci sono più cose nelle classi di modelli reali (elaborazione, callback e così via). Avere tale proprietà surrogata mi costringerebbe ad aggiungere controlli condizionali a tutto il codice rendendolo orribile ... – Centurion
Non si dovrebbe usare la gestione della memoria per controllare il comportamento del programma. La gestione della memoria serve per gestire la memoria e non altro. Se è necessario terminare una discussione o qualche altra operazione, è necessario predisporre un segnale esplicito in tal senso indipendente dalla gestione della memoria. Il tuo progetto precedente era rotto. –