NSMutable Array Help

Discussion in 'iOS Programming' started by AnonymousInUse, Oct 10, 2012.

  1. AnonymousInUse, Oct 10, 2012
    Last edited: Oct 10, 2012

    AnonymousInUse macrumors newbie

    Joined:
    Oct 9, 2012
    #1
    The crash repots telling me EXC_BAD_ACCESS so I know I have a crappy pointer(s) in this code but I have no clue how to fix it.

    Cloud is a UIImageView Subclass, a CADisplayLink is a timer that fires a method 60 times per second.

    My Header
    Code:
    @interface NormalGame : NSObject{
    NSMutableArray* cloudArray;
    NSMutableArray* possibleActions;
    Cloud* cloud;
    CADisplayLink* displayLink;
    NSInteger spawnMore;
    }
    
    My Implementation

    Code:
    
    typedef void(^ActionBlock)(void);
    
    -(void)spawnCloud{
        NSString* string = @"cloud.png";
        cloud = [[Cloud alloc] initWithName:string];
        [self.view insertSubview:cloud atIndex:0];
        [cloudArray addObject:cloud];
    }
    
    -(id)init{
        self = [super init];
        if (self) {
        displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(gameLoop:)];
        [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode: NSRunLoopCommonModes];
    
        cloudArray = [[NSMutableArray arrayWithCapacity:10] retain];
        possibleActions = [NSMutableArray arrayWithCapacity:2];
        
        [possibleActions addObject:^{
        [self spawnCloud];
        CGPoint cloudPoint1;
        cloudPoint1.x = 40;
        cloudPoint1.y = 600;
        cloud.center = cloudPoint1;
        }];
        [possibleActions addObject:^{
        [self spawnCloud];
        CGPoint cloudPoint1;
        cloudPoint2.x = 280;
        cloudPoint2.y = 600;
        cloud.center = cloudPoint2;
        }];
        [possibleActions retain];
    }
    
    -(void)gameLoop:(CADisplayLink *)sender{
        spawnMore++;
        if (spawnMore >= 280) {
            spawnMore -= 280;
    
          ActionBlock block = [possibleActions objectAtIndex:arc4random_uniform(possibleActions.count)];
            block();
        }
        for (cloud in cloudArray){
            if (cloud.center.y >= -100) {
                cloud.center = CGPointMake(cloud.center.x, cloud.center.y - 3;
        }
        else if (cloud.center.y >= -100) {
                [cloud removeFromSuperview];
                [cloudArray removeObject:cloud];
        }
    }
    
    if I just call [self spawnCloud] and then give it a position where the [possibleActions objectAtIndex:arc4random_uniform(possibleActions.count)] is it will work, so I know something is wrong with what I'm doing in the NSMutable Array. I did get EXC_BAD_ACCESS so I do have a bad pointer somewhere in the code
     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    1. Which version of Xcode?

    2. Using ARC or not? I'm guessing not, because you have at least one overt retain in your code. But guessing about code is always bad.

    3. I see no possibleActions here. It's undefined in any posted code. Maybe it's a global or a static variable. No one but you knows.

    4. Have you run your code with zombies enabled? If the cause of the bug is an over-release or under-retain, zombies should eventually show it.
     
  3. AnonymousInUse thread starter macrumors newbie

    Joined:
    Oct 9, 2012
    #3
    1. 4.5
    2. no
    3. whoops I forgot to write it in the post but it is in my header, its a NSMutable array.
    4. No I have not, actually I don't even know what zombies is
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    You're "correction" to the original post is missing a semicolon.
    Please post real code that's actually been compiled.


    Google search terms: ios zombies xcode 4


    The blocks you're adding to possibleActions are C blocks, which are not Objective-C objects. Furthermore, those C blocks have a limited scope, unless they are copied.

    You should start here:
    https://developer.apple.com/library...eral/Conceptual/DevPedia-CocoaCore/Block.html

    And you should probably read the entire Blocks Programming Topics. In the Using Blocks section, pay particular attention to the headings "Copying Blocks", and "Patterns to Avoid".

    Also see:
    http://developer.apple.com/library/...iveC/WorkingwithBlocks/WorkingwithBlocks.html

    You might be better off using NSInvocation, or make a suitable wrapper class for holding and invoking the C block.

    Google search terms: block objective-C site:developer.apple.com
     
  5. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #5
    Actually, blocks ARE objects. A block is interchangeable with id type. You can add block objects to arrays as the OP is doing, and the array will take ownership of them (which copies them from the stack to the heap)

    To quote the llvm.org article about blocks:

    (From http://clang.llvm.org/docs/BlockLanguageSpec.txt)

     
  6. AnonymousInUse thread starter macrumors newbie

    Joined:
    Oct 9, 2012
    #6
    I'll look over the docs but maybe I'm approaching this wrong and using blocks isn't a good idea. I've been homeschooling myself programming and I'm not so good at it. How would you approach call one of these objects of these two objects randomly?
     
  7. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #7

    I'm still getting used to the syntax of blocks, but I don't think these 2 lines are correct:


    Code:
            [possibleActions objectAtIndex:arc4random_uniform(possibleActions.count)];
            block();
    
    The way I read it, the first line will simply return the block as the result of the method, but won't do anything with it. I have no idea what the function call block() is supposed to do. I guess you defined a block variable somewhere named block, but you did not show that code.

    This code should invoke one of the blocks from your array:

    Code:
            ActionBlock theBlock = [possibleActions objectAtIndex:arc4random_uniform(possibleActions.count)];
            theBlock();
    
     
  8. AnonymousInUse thread starter macrumors newbie

    Joined:
    Oct 9, 2012
    #8
    Oh my god, this is embarrassing but I cut a lot of code out when writing this so it would've been less confusing an when writing this I forgot to write actionBlock block in it... Im sorry, it is in the actual code
     
  9. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #9
    Thanks, I'd somehow missed that.
     
  10. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #10
    The code as posted is incomplete. Post the complete section that's relevant, or we can't help you fix it.
     
  11. AnonymousInUse thread starter macrumors newbie

    Joined:
    Oct 9, 2012
    #11
    I believe the post is now correct, sorry again for the issue
     
  12. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #12
    This seems wrong, or unexplained:
    Code:
        [possibleActions addObject:^{
        [self spawnCloud];
        CGPoint [COLOR="Red"]cloudPoint1[/COLOR];
        [COLOR="Blue"]cloudPoint2[/COLOR].x = 280;
        [COLOR="blue"]cloudPoint2[/COLOR].y = 600;
        cloud.center = [COLOR="Blue"]cloudPoint2[/COLOR];
        }];
    
    Should cloudPoint1 really be cloudPoint2? Or is there a cloudPoint2 variable outside the block's scope that hasn't been posted?


    The use of the ivar 'cloud' to couple the blocks to the factory method spawnCloud is a dubious design choice, at best.


    The summary of what the whole complex structure (array of blocks, coupling via 'cloud', etc.) is doing seems to be, "Randomly pick one of two distinct coordinates, and make a Cloud instance whose center is at that coordinate". I'd probably do that using a simple if/else. If the number of outcomes was more than two, I'd probably use a switch statement. It's what we did in the old days, before blocks. Or we used an initialized C array of values, and did a table lookup.

    If the coordinates needed to be read from a file, I'd use a plist containing an array of coord-pairs (an array of arrays, where each sub-array holds two numbers). Read the plist once, keep it around. Randomly pick one coord-pair from the array. Make the Cloud instance, set its center. Done.
     
  13. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #13
    It is recommended that you don't cut out code. Either cut-and-paste entire code or, if you're trying to simplify it or are dealing with non-disclosure, provide code from a stand-alone test project made specifically to replicate the issue.
     

Share This Page