Question

I've run into what I believe is a bug with UICollisionBehavior in UIKit. Adding them to an array of UIViews leads to a memory leak. I put together a simple demo project that creates 10 animations of a group of views falling with gravity applied, and a collision with the enclosing view's bounds. (Code below.) The Leaks template in Instruments reports nine 64-byte leak for each run.

- (void)doAnimation
{
    self.animateButton.enabled = NO;

    CGFloat left = 12.0f;
    NSMutableArray *items = [NSMutableArray new];

    // set up an array of views and add them to the superview
    while (left < self.view.bounds.size.width - 12.0f) {
        UIView *view = [[UIView alloc] initWithFrame:CGRectMake(left, 70, 32, 32)];
        left += 34.0f;
        [self.view addSubview:view];
        view.backgroundColor = [UIColor grayColor];
        [items addObject:view];
    }

    // create a gravityBehavior and initialize with views array
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:items];
    [self.animator addBehavior:gravity];

    // create a collisionBehavior and initialize with views array
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:items];
    collision.translatesReferenceBoundsIntoBoundary = YES;
    [self.animator addBehavior:collision];
}

// UIDynamicAnimatorDelegate method that's called when collision animation is complete
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator
{
    // get a collision behavior in order to access its items for loop below
    UICollisionBehavior *behavior;
    for (UIDynamicBehavior *oneBehavior in animator.behaviors) {
        if ([oneBehavior isKindOfClass:[UICollisionBehavior class]]) {
            behavior = (UICollisionBehavior *)oneBehavior;
            break;
        }
    }

    // reset the UIDynamicAnimator property's behaviors for next run
    [self.animator removeAllBehaviors];

    self.dropCount++;

    // remove all subviews
    for (UIView *view in behavior.items) {
         [view removeFromSuperview];
    }

    // run the animation again or break
    if (self.dropCount < 10) {
        [self doAnimation];
    } else {
        self.animateButton.enabled = YES;
    }
}

I'd really like to be able to implement collisions in an app I'm working on, but this leak makes it unusable. I have tried saving the collisionBehavior in property and reusing it. That prevents leaking all but one 64-byte chunk of memory, but the collisions no longer work when that's done. Can anyone suggest a workaround that works?

Was it helpful?

Solution

I ran into the same issue but fixed the memory leak by setting the boundaries using UICollisionBehavior's

addBoundaryWithIdentifier

Unfortunately setting boundaries with translatesReferenceBoundsIntoBoundary and setTranslatesReferenceBoundsIntoBoundaryWithInsets caused leaks for me.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top