5

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

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

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。

- 我是否在某处保留另一个静态布尔副本? 当我在方法范围内将块声明为静态时,分配了我的第一个布尔值。这完全取决于我没有研究过的 Objective-C 的激活记录方案。但是假设是这样,当在 popViewcOntroller 上释放对象时,该副本就消失了。当字典被杀死时,应该在将它添加到字典时从块上调用副本来释放它的另一个副本?

我是否保留了整个小册子对象本身?我没有完全从 Apple 文档中得到它,但他们说如果我通过引用访问实例变量,我会保留 self。我尝试从块内释放自我,一切都运行良好......?

4

2 回答 2

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];
}
于 2012-04-22T23:49:19.753 回答
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
于 2012-04-23T00:40:00.757 回答