I'm following the "Your Second iOS App" and I decided to play with the code to understand Objective C well...
What I'm trying to do is simply adding an object to a mutable array in a class. Here are the classes:
BirdSighting.h
#import <Foundation/Foundation.h>
@interface BirdSighting : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *location;
@property (nonatomic, copy) NSDate *date;
-(id) initWithName: (NSString *) name location:(NSString *) location date:(NSDate *) date;
@end
BirdSighting.m
#import "BirdSighting.h"
@implementation BirdSighting
-(id) initWithName:(NSString *)name location:(NSString *)location date:(NSDate *)date
{
self = [super init];
if(self) {
_name = name;
_location = location;
_date = date;
return self;
}
return nil;
}
@end
BirdSightingDataController.h
#import <Foundation/Foundation.h>
@class BirdSighting;
@interface BirdSightingDataController : NSObject
@property (nonatomic, copy) NSMutableArray *masterBirdSightingList;
- (NSUInteger) countOfList;
- (BirdSighting *) objectInListAtIndex: (NSUInteger) theIndex;
- (void) addBirdSightingWithSighting: (BirdSighting *) sighting;
@end
BirdSightingDataController.m
#import "BirdSightingDataController.h"
@implementation BirdSightingDataController
- (id) init {
if(self = [super init]) {
NSMutableArray *sightingList = [[NSMutableArray alloc] init];
self.masterBirdSightingList = sightingList;
return self;
}
return nil;
}
-(NSUInteger) countOfList
{
return [self.masterBirdSightingList count];
}
- (BirdSighting *) objectInListAtIndex: (NSUInteger) theIndex
{
return [self.masterBirdSightingList objectAtIndex:theIndex];
}
- (void) addBirdSightingWithSighting: (BirdSighting *) sighting
{
[self.masterBirdSightingList addObject:sighting];
}
@end
And this is where I'm trying to add a BirdSighting instance to the mutable array:
#import "BirdsMasterViewController.h"
#import "BirdsDetailViewController.h"
#import "BirdSightingDataController.h"
#import "BirdSighting.h"
@implementation BirdsMasterViewController
- (void)awakeFromNib
{
[super awakeFromNib];
BirdSightingDataController *dataController = [[BirdSightingDataController alloc] init];
NSDate *date = [NSDate date];
BirdSighting *sighting = [[[BirdSighting alloc] init] initWithName:@"Ebabil" location:@"Ankara" date: date];
[dataController addBirdSightingWithSighting: sighting];
NSLog(@"dataController: %@", dataController.masterBirdSightingList);
self.dataController = dataController;
}
..........
@end
It throws NSInvalidArgumentException in BirdSightingDataController addBirdSightingWithSighting method...
What am I doing wrong?
This is the full debug output: 2012-11-26 01:22:38.495 BirdWatching[4597:c07] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x71899d0 2012-11-26 01:22:38.496 BirdWatching[4597:c07] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x71899d0'
This line:
BirdSighting *sighting = [[[BirdSighting alloc] init] initWithName:@"Ebabil" location:@"Ankara" date: date];
should be:
BirdSighting *sighting = [[BirdSighting alloc] initWithName:@"Ebabil" location:@"Ankara" date: date];
You can't call two initializers.
Update:
Based on the complete error message I now see the problem. The NSMutableArray
you think you have for the property is really an NSArray
. This is caused by defining the property with copy
.
When you define copy
for a property, it makes an immutable copy of the object. So when you assign your NSMutableArray
, an immutable copy is made and then assigned to the instance variable. So you end up assigning an NSArray
, not an NSMutableArray
.
A fix is to change the property definition from:
@property (nonatomic, copy) NSMutableArray *masterBirdSightingList;
to:
@property (nonatomic, strong) NSMutableArray *masterBirdSightingList;