Objective-C块内的静态变量?

时间:2012-04-22 23:15:50

标签: objective-c cocoa-touch objective-c-blocks

我正在研究一些CoreAnimation的东西。带有几个视图控制器的导航控制器。并且视图控制器具有用于“小册子”的不同“页面”的UISCrollView。在每个页面上,可能会有一些动画在用户翻转到该页面时被触发。

我正在尝试这样的事情(对于一次性动画)。

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];
为了便于解释,“动画的名称”和“某些页码”是占位符(它们是任意的NSString文字)。

触发这些动画的代码是:

- (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];
 }

到目前为止一切顺利,只有如果我从导航控制器弹出小册子(也就是在小册子上调用dealloc)然后再将其推入,静态布尔仍然会被设置。

我的想法:

- 我保留了阻止吗?  我不知道..我在将它(带副本)添加到字典后调用release,并且小册子的dealloc方法调用字典上的release。

- 我在某处保留了静态bool的另一个副本吗? 当我在方法范围内将块声明为静态时,我的第一个bool被分配。很好地取决于Objective-C的激活记录方案,我没有考虑过。但假设是这样,当在popViewcOntroller上释放对象时,该副本就消失了。当字典被删除时,应该在将字符串中的副本添加到字典时释放它的另一个副本吗?

我是否保留了整个宣传册对象?我没有完全从Apple文档中获取它,但他们说如果我通过引用访问实例变量,我会保留自己。我尝试从块中释放自我,一切都运行得很好......?

2 个答案:

答案 0 :(得分:1)

使块返回一个值,然后决定是否从字典中删除该块。

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

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

让我们测试静态变量

@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];
}

此输出

#=> 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

我们如何解决问题?一旦像这样执行了

,我可能会从字典中删除该对象
- (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];
}

答案 1 :(得分:0)

您的评论

  

到目前为止一直很好,只有如果我从导航中弹出小册子   控制器(也称为小册子上的dealloc),然后将其推入   再次,静态布尔仍然设置。

似乎暗示你想在推送的视图控制器上有一个BOOL成员变量......如果这是正确的,你可以使用objc_setAssociatedObject()实现这一点。

我可能会在UIViewController中添加一个类别来实现这个目的:

@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