2

When executing

[self.blockViews addObject:curBlockView];

I get an error

2011-07-01 13:35:26.240 Block Breaker[42061:207] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x4e037a0

I am pretty new to Objective-C. Is it something in my init method?

//
//  GameEngine.h
//  Block Breaker
//
//  Created by Chris Muench on 7/1/11.
//  Copyright 2011 N/A. All rights reserved.
//

#import <Foundation/Foundation.h>


@interface GameEngine : NSObject {
    NSMutableArray *blockViews;
    int numBlockRows;
    int score;
}
@property (nonatomic, copy) NSMutableArray *blockViews;
@property int numBlockRows;
@property int score;

- (void) setup;
- (void) setupBlocks;
@end


//
//  GameEngine.m
//  Block Breaker
//
//  Created by Chris Muench on 7/1/11.
//  Copyright 2011 N/A. All rights reserved.
//

#import "GameEngine.h"
#import "Block.h"
#import "BlockView.h"


@implementation GameEngine
@synthesize blockViews;
@synthesize numBlockRows;
@synthesize score;

- (id) init
{
    if ((self = [super init])) 
    {
        self.blockViews = [[NSMutableArray alloc] init];
        self.numBlockRows = 2;
        self.score = 0;
    }
    return self;
}

- (void) setup
{
    [self setupBlocks];
}

- (void) setupBlocks
{
    float blockWidth = 10;
    float blockHeight = 10;
    float rowSpacing = 2;
    float colSpacing = 2;
    float currentX = 0;
    float currentY=10;
    float screenWidth = 200;

    for (int rowCounter=0;rowCounter<self.numBlockRows;rowCounter++)
    {

        while(currentX <=screenWidth)
        {
            Block *curBlock = [[Block alloc] initWithWidth:blockWidth height:blockHeight];
            BlockView *curBlockView = [[BlockView alloc] initWithFrame:CGRectMake(currentX, currentY, curBlock.width, curBlock.height)];
            curBlockView.block = curBlock;

            [self.blockViews addObject:curBlockView];           
            currentX+=blockWidth+colSpacing;
            [curBlock release];
            [curBlockView release];
        }

        currentX=0;
        currentY+=blockHeight+rowSpacing;
    }


}

@end
jscs
  • 63,694
  • 13
  • 151
  • 195
Chris Muench
  • 17,444
  • 70
  • 209
  • 362
  • It seems if I change @property (nonatomic, copy) NSMutableArray *blockViews, to retain, the error goes away. but why? – Chris Muench Jul 01 '11 at 17:39
  • Think again, in many cases you don't need to set a mutable array. The array is mutable and that's why you only need to "access" its pointer to add new objects. Apart from that, you don't need to use the setters inside `init`. You can reference the ivars directly there. – sidyll Jul 01 '11 at 17:47
  • 2
    You don't want to do self.blockviews = [[NSMutableArray alloc] init]; regardless of the copy/retain issue that people have answered below. You'll leak the memory eventually. Use either blockViews = [[NSMutableArray alloc] init]; or self.blockViews = [NSMutableArray array]; – InsertWittyName Jul 01 '11 at 17:48
  • See this discussion: http://stackoverflow.com/questions/2399490/what-is-the-difference-between-copy-and-retain – Saikat Jul 01 '11 at 17:48
  • @sidyll: Actually, it makes a lot of sense from an encapsulation point of view to copy mutable arrays when passing them between objects. Otherwise they can each modify the same instance of the array, which very quickly gets hard to keep track of. – jscs Jul 01 '11 at 17:56
  • @John Caswell I agree completely, but what I was referring is that many times you only need a *readonly* property to add objects to that array. – sidyll Jul 01 '11 at 18:00
  • @sidyll: I see -- you're quite right. For some reason I understood this sample object to be the one which was _receiving_ the array, rather than the source of it. – jscs Jul 01 '11 at 18:11
  • No problems @John Caswell. And thanks for providing so many constructive details, both in the answer and here in the comments. – sidyll Jul 01 '11 at 18:23
  • See my answer here http://stackoverflow.com/questions/4893043/iphone-crash-using-addobject-on-a-nsmutablearray/31871614#31871614 – Narasimha Nallamsetty Aug 07 '15 at 07:10

3 Answers3

10

When you copy an NSMutableArray using the copy method, or a synthesized setter for which the property was specified as copy, you get an immutable copy, which means you essentially end up with a plain NSArray.* There is a method mutableCopy which will preserve the mutability, but I don't believe there's any way to specify that for a property.

If you want your array to be mutably copied when you set it, you'll have to write your own setter method, and specify that method in the property declaration.

@property (nonatomic, copy, setter=setBlockViewsByCopying) NSMutableArray * blockViews;

- (void) setBlockViewsByCopying: (NSMutableArray *)newBlockViews {
    NSMutableArray * tmp = [newBlockViews mutableCopy];
    [blockViews release];
    blockViews = tmp;
}

A side note, as @InsertWittyName mentioned in a comment, your code initializing blockViews will create a leak, because you have two claims of ownership on the array you're creating -- one for the alloc and one for the retain or copy that you get using the property:

self.blockViews = [[NSMutableArray alloc] init];
//  ^ One claim                    ^ Two claims
// Only one release later, when the property is set again!
// LEAK!

You should instead do:

self.blockViews = [NSMutableArray array];
// Creates an object you don't own which you then make one claim
// on using the setter

*Though as it is a class cluster, you really get some unspecified concrete subclass.

jscs
  • 63,694
  • 13
  • 151
  • 195
0

Copy should retain, If I remember correctly.

Anyway using copying cannot be optimal, every time you call the accessor (set), you got a copy, and, as well pointed out above, is immutable.

Adding is fine, but remember to release the array when done.

ingconti
  • 10,876
  • 3
  • 61
  • 48
-1

Did you retain your array ??

Legolas
  • 12,145
  • 12
  • 79
  • 132