2012-04-22 21 views
5

Estoy trabajando en algunas cosas de CoreAnimation. Un controlador de navegación con algunos controladores de vista. Y los controladores de vista tienen UISCrollViews para diferentes "páginas" de un "folleto". En cada página, puede haber algo de animación que se activa cuando el usuario cambia a esa página.variables estáticas dentro de bloques Objective-C?

Estaba intentando algo como esto (para animaciones de un golpe).

void (^animationBlock)() = 
    ^{ 
    static bool alreadyTriggered = NO; 
    if(alreadyTriggered) 
     return; 

    [CATransaction begin]; 
    [CATransaction setCompletionBlock: 
    ^{ 
     alreadyTriggered = YES; 
    }]; 

    // Do me some animations... 

    [CATransaction commit]; 
    }; 

    NSMutableDictionary* pageBlocks = [[NSMutableDictionary alloc] init]; 
    [pageBlocks setObject:[animationBlock copy] forKey:<animation's name>]; 
    [self.animationBlocks setObject:pageBlocks forKey:<some page number>]; 

    [pageBlocks release]; 
    [animationBlock release]; 

"El nombre de la animación" y "algún número de la página" son marcadores de posición para el bien de la explicación (que son literales NSString arbitrarias).

Y el código que desencadena estas animaciones es:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{ 
    int pageNumber = floor(self.scrollView.contentOffset.x/self.scrollView.frame.size.width); 
    NSMutableDictionary* pageBLocks = [self.animationBlocks objectForKey:[NSString stringWithFormat:@"page%i",pageNumber]]; 
    [CATransaction begin]; 
    for(id key in pageBLocks) 
    ((void (^)())[pageBLocks objectForKey:key])(); 
    [CATransaction commit]; 
} 

Hasta aquí todo bien, solo que si hago estallar el folleto del mando de navegación (llamadas también conocido como dealloc en el folleto) ya continuación presiónelo de nuevo , el bool estático todavía está configurado.

Mis pensamientos:

- Am I conservando el bloque? No lo sé .. Estoy llamando a la versión después de agregarlo (con copia) al diccionario y también el método dealloc del folleto llama a la versión del diccionario.

- ¿Estoy guardando otra copia del bool estático en alguna parte? Mi primer bool se asigna cuando declaro el bloque como estático dentro del alcance de un método ... bien depende del esquema de registro de activación de Objective-C que no he examinado. Pero suponiendo que sí, esa copia se ha ido al liberar el objeto en popViewcOntroller. ¿Y otra copia de la invocación de copia en el bloque cuando se agrega al diccionario debería ser liberada cuando se mata el diccionario?

Estoy reteniendo todo el objeto del folleto en sí? No lo obtuve completamente de los documentos de Apple, pero dicen que si accedo a una variable de instancia por referencia me quedo. Intenté soltarme desde el interior del bloque y todo sigue funcionando bien ...

+0

no creo que se debe mezclar con '' copy' Block_release() '[bloques de programación temas] (http: // desarrolladores. apple.com/library/ios/#documentation/Cocoa/Conceptual/Blocks/Articles/bxUsing.html#//apple_ref/doc/uid/TP40007502-CH5-SW1) –

+0

¿eh? Esa es la forma de mover un bloque de la pila al montón – SaldaVonSchwartz

+0

El documento sugiere que debe 'equilibrar un Block_copy() con Block_release()' y 'balance copiar o mantener con release (o liberación automática)' –

Respuesta

1

Haga que el bloque devuelva un valor y luego decida si eliminar o no el bloque del diccionario.

// ... 
    BOOL shouldRemove = block(); 

    if (shouldRemove) { 
     [pageBlocks removeObjectForKey:key]; 
    } 
// ... 

Vamos a probar la variable estática

@interface TestClass : NSObject 
@end 

@implementation TestClass 

- (void(^)(void))block; 
{ 
    return [[^{ 

     static BOOL staticBOOL = NO; 

     NSLog(@"%d", staticBOOL); 

     staticBOOL = YES; 

    } copy] autorelease]; 
} 

@end 

int main(int argc, char *argv[]) { 
    NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; 

    TestClass *test1 = [[TestClass alloc] init]; 
    test1.block(); 
    test1.block(); 

    TestClass *test2 = [[TestClass alloc] init]; 
    test2.block(); 
    test2.block(); 

    [p release]; 
} 

Esto da salida a

#=> 2012-04-23 00:43:38.501 Untitled[8380:707] 0 
#=> 2012-04-23 00:43:38.503 Untitled[8380:707] 1 
#=> 2012-04-23 00:43:38.503 Untitled[8380:707] 1 
#=> 2012-04-23 00:43:38.504 Untitled[8380:707] 1 

¿Cómo resolver el problema? Probablemente me retire el objeto del diccionario una vez que había sido ejecutado como esto

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 
{ 
    NSInteger pageNumber = floor(self.scrollView.contentOffset.x/self.scrollView.frame.size.width); 
    NSMutableDictionary *pageBlocks = [self.animationBlocks objectForKey:[NSString stringWithFormat:@"page%i", pageNumber]]; 

    [CATransaction begin]; 

    for (id key in [pageBlocks copy]) { 
     void (^block)(void) = [pageBlocks objectForKey:key]; 
     if (block) { 
      block(); 
     } 
     [pageBlocks removeObjectForKey:key]; 
    } 

    [CATransaction commit]; 
} 
+0

justo pensé en eso, pero el problema no es que todos los bloques de animación sean "de una sola vez". Algunos deberían volver a ejecutar cada vez y no ser eliminados del diccionario. Así que necesito una forma para que el bloque se establezca en un solo disparo o no. – SaldaVonSchwartz

+0

¿Dos diccionarios diferentes? –

+0

ehhh ... quiero decir, claro ... pero no es lo suficientemente elegante (para mi gusto dentro del alcance de este proyecto, al menos, sin ofender). Mi idea era que el bloque se comporta como un hilo y debe tener su propia pila de géneros ... así que debe haber una forma de mantener el estado interno a lo largo de las llamadas – SaldaVonSchwartz

0

Su comentario

Hasta aquí todo bien, solo que si hago estallar el folleto del mando de navegación (aka llama a dealloc en el folleto) y luego empújelo nuevamente en , el bool estático todavía está configurado.

parece dar a entender que desea una variable miembro de BOOL en los controladores de vista empujado ... Si eso es correcto se puede lograr esto usando objc_setAssociatedObject().

podría añadir una categoría para UIViewController para lograrlo:

@interface UIViewController (OneShotAnimation) 
@property (nonatomic) BOOL animationHasBeenPlayed ; 
@end 

@implementation UIViewController (OneShotAnimation) 

-(void)setAnimationHasBeenPlayed:(BOOL)b 
{ 
    objc_setAssociatedObject(self, @"animationHasBeenPlayed", [ NSNumber numberWithBool:b ], OBJC_ASSOCIATION_RETAIN_NONATOMIC) ; 
} 

-(BOOL)animationHasBeenPlayed 
{ 
    return [ objc_getAssociatedObject(self, @"animationHasBeenPlayed") boolValue ] ; 
} 

@end 
+0

oh ... pero el folleto es su controlador de vista presionada, y está siendo desasignado ... así que esto no ayudará – nielsbot

+0

Correcto ... como le dije a Paul, el bool no es para el controlador de visualización. El bool fue pensado como una forma para que los muchos bloques mantenidos en el diccionario de los controladores pudieran saber si deberían volver a jugar o no. Se supone que es parte del "estado interno" de un bloque independientemente del controlador de vista en el que se declare que está dentro. Así que un bool por bloque – SaldaVonSchwartz

+0

parece que su bloque necesita una variable miembro. Todavía puede usar 'objc_setAssociatedObject' – nielsbot