Um simples exemplo de como adicionar dias a um NSDate:
|
1 2 3 4 5 6 |
int dias = 1;
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:dias];
NSCalendar *calendario = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *novaData = [calendario dateByAddingComponents:components toDate:[NSDate date] options:0]; |
|
1 2 3 |
"_OBJC_CLASS_$_XXX", referenced from:
clang: error: linker command failed with exit code 1 (use -v to see invocation) |
Este erro ocorre pois a classe copiada para o projeto não foi adicionada ao Target. Para corrigir, basta selecionar a implementação (.m) e no painel direito (Utilities)
marcar a opção Target Membership

|
1 |
"ld: duplicate symbol _OBJC_METACLASS_$_XXX..." |
Este erro normalmente ocorre quando a mesma classe existe no projeto em grupos diferentes, bastando remover uma das cópias para resolver.
Em alguns casos, precisamos cancelar o processamento devido a problemas nos dados. Normalmente o Core Data já cuida do rollback em caso de erro, porém em certos casos precisamos fazer este controle manualmente.
Abaixo os passos para a execução de um rollback manual:
Declare em sua interface uma propriedade do tipo NSUndoManager e um NSManagedObjectContext:
|
1 2 |
@property (strong, nonatomic) NSUndoManager *undoManager;
@property (strong, nonatomic) NSManagedObjectContext *moc; |
Método exemplificando o rollback:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
- (void)rollbackMethod
{
NSError *errorsMethod1;
NSError *errorsMethod2;
// inicializa o NSUndoManager
if (undoManager == nil) {
undoManager = [[NSUndoManager alloc] init];
// moc é um NSManagedObjectContext
[moc setUndoManager:undoManager];
}
// agrupa a execução dos métodos
[undoManager beginUndoGrouping];
// executa o primeiro método
errorsMethod1 = [self method1];
// executa o segundo método
errorsMethod2 = [self method2];
[undoManager endUndoGrouping];
// verifica se ocorreram erros na execução dos métodos
if (errorsMethod1 != nil || errorsMethod1 != nil) {
// executa o rollback
[undoManager undo];
}
}
} |
O que acontece no método acima é que agrupamos a execução de nossos métodos com o undoManager e caso um deles falhe, forçamos o rollback com o comando undo.
Core data é uma das poucas tecnologias que realmente facilitam a vida do desenvolvedor, inclusive sinto falta quando estou desenvolvendo para Android ou Windows Phone.
Porém, nem tudo são flores, quando precisamos realizar operações pesadas que incluem o banco de dados, precisamos utilizar Multithreading e tomar certas precauções para que a interface do usuário não congele e os dados sejam atualizados de maneira correta.
Não vou entrar a fundo no processo de multithreading com Core Data, porém, deixo aqui a documentação de referência.
De acordo com a documentaçõa existem 2 maneiras para que nossa aplicação se comporte de maneira correta em um ambiente multithreading:
Aqui vou falar como implementar o primeiro caso, por ser o mais utilizado devido a baixa complexidade.
Dentro de sua interface, crie um novo NSManagedObjectContext:
|
1 |
@property (strong, nonatomic) NSManagedObjectContext *backgroundContext; |
Abaixo 2 métodos que serão utilizados no tratamento do novo NSManagedObjectContext:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
- (void)initBackgroundContext
{
if (backgroundContext == nil) {
backgroundContext = [[NSManagedObjectContext alloc] init];
[backgroundContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
/* Salve o contexto secundário e trata as notificações de save no banco */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];
}
}
/* Trata a notificação de salva no banco de dados vinda do contexto secundário */
- (void)backgroundContextDidSave:(NSNotification *)notification {
/* Garante que estamos na thread principal quando atualizarmos o contexto principal */
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
withObject:notification
waitUntilDone:NO];
return;
}
/* Faz o merge das modificações feitas no contexto secundário com o contexto principal */
[self.moc mergeChangesFromContextDidSaveNotification:notification];
} |
Antes de iniciar o processamento pesado, inicialize o novo NSManagedObjectContext:
|
1 2 3 4 5 6 7 8 9 10 11 |
- (void)method
{
// inicializa o contexto secundário
[self performSelector:@selector(initBackgroundContext)];
// Cria uma NSInvocationOperation para processamento em pararelo.
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method:) object:params];
// inicializa a fila para processamento fora da thread principal com a NSInvocationOperation criada anteriormente.
[self performSelector:@selector(startQueue:) withObject:operation];
} |
Para fazer a execução em pararelo utilizaremos um NSOperationQueue. Declare-o em sua interface:
|
1 |
@property (strong, nonatomic) NSOperationQueue *queue; |
Abaixo os método que inicializa a fila de processamento pararelo:
|
1 2 3 4 5 6 7 8 9 10 |
- (void)startQueue:(NSOperation *)operation
{
if (queue == nil) {
queue = [[NSOperationQueue alloc] init];
}
[queue setMaxConcurrentOperationCount:1]; // número de operações em pararelo
[queue addOperation:operation];
[queue addObserver:self forKeyPath:@"operations" options:0 context:nil];
} |
Para finalizar, o método que verifica o término do processamento pararelo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"operations"]) {
// verifica se o processamento em pararelo temrinou
if ([queue.operations count] == 0) {
// TODO: execução de métodos após o processamento da fila
// aqui um método opcional para executar operações na thread principal, como mudança de componentes visuais.
dispatch_async(dispatch_get_main_queue(), ^{
});
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
} |
Pequeno trecho de código para quem precisa remover a sombra em volta do UIWebView
|
1 2 3 4 5 |
for(UIView *view in [[[webView subviews] objectAtIndex:0] subviews]) {
if([view isKindOfClass:[UIImageView class]]) {
view.hidden = YES;
}
} |