I am new to programming with Objective-C and Stackoverflow, and I need some help.
I'm trying to get an object from a NSMutableArray and check one of its internal properties. I figured the best way to do this is by doing the following cast:
GenericRoom *room = (GenericRoom*)[myRooms objectAtIndex: currentIndex + side];
if (room.myType == EMPTY) {
return YES;
}
The problem is that when I create a class of type GenericRoom, your constructor already defines myType as EMPTY. When I insert the instances in NSMutrableArray, I changed to myType TAVERN this way:
NSMutableArray *myRooms = [[NSMutableArray alloc] initWithCapacity:15];
for (int i = 0: i <15: i + +) {
GenericRoom tmpRoom * = [[GenericRoom alloc] initWithType: TAVERN];
myRooms insertObject: tmpRoom atIndex i];
}
But when I do the cast that quote up there, it simply creates a new instance of the object GenericRoom, instead of copying the object inside NSMutableArray, making its result is always YES, my failing vereficação.
Is there any better way to solve this problem?
Thank you all.
EDIT: The complete code
GenericRoom.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
enum Rooms {
EMPTY, TAVERN, WARRIORGUILD, MAGEGUILD
}roomsType;
#interface GenericRoom : CCLayer {
CCSprite *mySprite;
enum Rooms myType;
}
#property (nonatomic, retain) CCSprite *mySprite;
#property enum Rooms myType;
#property int test;
- (id)initWithSprite: (NSString *)file;
- (id)initWithType: (enum Rooms)roomType;
#end
GenericRoom.m
#import "GenericRoom.h"
#implementation GenericRoom
#synthesize mySprite, myType, test;
-(id)init {
if (self = [super init]) {
}
return self;
}
-(id)initWithType: (enum Rooms) roomType {
if (self = [super init]) {
myType = roomType;
}
return self;
}
-(id)initWithSprite: (NSString *)file {
if (self = [super init]) {
mySprite = [CCSprite spriteWithFile:file];
[self addChild: mySprite];
}
return self;
}
#end
RoomManager.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "GenericRoom.h"
#import "RoomTavern.h"
#import "RoomEmpty.h"
enum Sides {
LEFT, RIGHT, UP, DOWN
}theSides;
#interface RoomManager : CCLayer {
CGSize size;
NSMutableArray *myRooms;
}
- (void) CreateRoom: (enum Rooms)roomType;
#end
RoomManager.m
#implementation RoomManager
-(id)init {
if (self = [super init]) {
size = [[CCDirector sharedDirector] winSize];
myRooms = [[NSMutableArray alloc] initWithCapacity:15];
for (int i = 0; i < 15; i++) {
GenericRoom *tmpRoom = [[GenericRoom alloc] initWithType:TAVERN];
tmpRoom.test = 10;
[myRooms insertObject:tmpRoom atIndex:i];
}
[self CreateRoom:TAVERN];
}
return self;
}
- (void) CreateRoom: (enum Rooms)roomType {
switch (roomType) {
case TAVERN:
{
//Create the Tavern Main Room
RoomTavern *tmpRoom = [[RoomTavern alloc] initWithSprite:#"room-hd.png"];
tmpRoom.mySprite.position = ccp(size.width/2,size.height/2);
[self addChild:tmpRoom];
[myRooms removeObjectAtIndex:8];
[myRooms insertObject:tmpRoom atIndex:8];
if ([self CheckAdjacentRooms:8 andSide:LEFT]) {
RoomEmpty *tmpEmptyRoom = [[RoomEmpty alloc] initWithSprite:#"roomToBuild-hd.png"];
tmpEmptyRoom.mySprite.position = ccp(tmpRoom.mySprite.position.x - tmpRoom.mySprite.contentSize.width, tmpRoom.mySprite.position.y);
[self addChild:tmpEmptyRoom];
[myRooms insertObject:tmpEmptyRoom atIndex:7];
}
break;
}
default:
break;
}
}
- (BOOL) CheckAdjacentRooms: (int)currentIndex andSide:(enum Sides)side {
int leftRightSide = 0;
if(side == LEFT)
leftRightSide = -1;
else if (side == RIGHT)
leftRightSide = 1;
GenericRoom *roomTmp = (GenericRoom *)[myRooms objectAtIndex:currentIndex + side];
if (roomTmp.myType == EMPTY) {
return YES;
}
else
return NO;
}
#end
To enhance #Dan F answer. Read this ...
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html#//apple_ref/doc/uid/TP30001163-CH11-SW7
... what's nil messaging. Your locally declared myRooms variable hides your myRooms property (= instance variable). In other words, myRooms is nil. And what happens ...
GenericRoom *room = (GenericRoom*)[myRooms objectAtIndex: currentIndex + side];
Because of nil messaging rules, [myRooms objectAtIndex:...] returns nil, thus your room is set to nil.
if (room.myType == EMPTY) {
return YES;
}
And room.myType sends message myType to room object. And because room is nil, return value is 0 - enums does contain integer scalar values - this is the reason why it returns 0. And I assume that EMPTY is first element in your enum = has value 0. And 0 == 0 => returns YES even when your myRooms instance variable is nil.
If your code above is how it appears in your actual project, then the problem is you are re-declaring your myRooms object. When you write this line:
NSMutableArray *myRooms = [[NSMutableArray alloc] initWithCapacity:15];
you are creating a new locally-scoped object named myRooms that is lost when you exit the current scope (method, block, etc.)
What you need to do to fix this is simply remove the NSMutableArray * at the beginning of the line, and it will assign the newly allocated array to your property. Like so:
myRooms = [[NSMutableArray alloc] initWithCapacity:15];
Your initialiser seems to have some flaws, that might cause your issues.
// NOTE: `Rooms` instead of `enum Rooms`
- (id)initWithType:(Rooms)roomType
{
if (self = [super init])
{
// NOTE: accessor is used, otherwise the value is cleared when out of scope
self.myType = roomType;
}
return self;
}
Related
The class "PlayingCardDeck" inherits "Deck" class can not put the method
- (void) addCard:(Card *)card atTop:(BOOL)atTop
Xcode accuses me the following error:
no Known class method for selector 'addCard:atTop'
Class: PlayingCardDeck
#import "PlayingCardDeck.h"
#import "PlayingCard.h"
#implementation PlayingCardDeck
- (id)init
{
self = [super init];
if(self) {
for(NSString *suit in [PlayingCard validSuits]){
for (NSUInteger rank = 1; rank <= [PlayingCard maxRank]; rank++){
PlayingCard *card = [[PlayingCard alloc] init];
card.rank = rank;
card.suit = suit;
[PlayingCardDeck addCard:card atTop:NO];
}
}
}
return self;
}
#end
Class: Deck
#import "Deck.h"
#interface Deck()
#property (strong, nonatomic) NSMutableArray *cards;
#end
#implementation Deck
- (NSMutableArray *)cards
{
if(!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void) addCard:(Card *)card atTop:(BOOL)atTop
{
if(atTop){
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
- (Card *)drawRandomCard
{
Card *randomCard = nil;
if(self.cards.count){
unsigned index = arc4random() % self.cards.count;
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
#end
thank you all for the help.
The method -addCard:atTop: is an instance method. It must be sent to an instance of a class, not the class itself. This line:
[PlayingCardDeck addCard:card atTop:NO];
is attempting to send it to the class PlayingCardDeck, not any instance of that class. That's what the error is trying to tell you.
Since that code is in the -init method of the PlayingCardDeck class, you probably meant to send the message to self:
[self addCard:card atTop:NO];
I have a IKImageBrowserView which has its own controller - BrowserController.h + .m
#interface BrowserController : NSWindowController{
NSMutableArray *_images;
}
#property (strong,nonatomic) IBOutlet IKImageBrowserView *imageBrowser;
-(void)addMultipleImages;
When I run the app for the first time, the staring image loads, but when I click a button to add other images and call a method from another class I don't get any results. I have noticed that my _imageBrowser loses the reference and becomes nil.
How could I solve this issue?
AppDelegate.m
#import "AppDelegate.h"
#import "BrowserController.h"
#implementation AppDelegate{
BrowserController *imageBrowserController;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
imageBrowserController = [BrowserController sharedManager];
}
- (IBAction)doSomething:(id)sender {
[imageBrowserController addMultipleImages];
}
#end
BrowserController.m
#import "BrowserController.h"
#interface myImageObject : NSObject
{
NSString *_path;
}
#end
#implementation myImageObject
/* our datasource object is just a filepath representation */
- (void)setPath:(NSString *)path
{
if(_path != path)
{
_path = path;
}
}
/* required methods of the IKImageBrowserItem protocol */
#pragma mark -
#pragma mark item data source protocol
/* let the image browser knows we use a path representation */
- (NSString *)imageRepresentationType
{
return IKImageBrowserPathRepresentationType;
}
/* give our representation to the image browser */
- (id)imageRepresentation
{
return _path;
}
/* use the absolute filepath as identifier */
- (NSString *)imageUID
{
return _path;
}
#end
#interface BrowserController ()
#end
#implementation BrowserController
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void)awakeFromNib{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
- (void)updateDataSource{
[_imageBrowser reloadData];
}
-(NSUInteger) numberOfItemsInImageBrowser:(IKImageBrowserView *)aBrowser{
return [_images count];
};
-(id)imageBrowser:(IKImageBrowserView *)aBrowser itemAtIndex:(NSUInteger)index{
return [_images objectAtIndex:index];
};
- (void)updateDatasource
{
[_imageBrowser reloadData];
}
- (void)addMultipleImages{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[_images addObject:p];
[_images addObject:p];
[_imageBrowser reloadData];
}
+ (id)sharedManager {
static BrowserController *sharedMyManager = nil;
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
#end
There is some confusion as to the name of the class where you mention it's called ImageBrowserController at the start of your question and it looks like it's called BrowserController at the end of your question.
The issue is that you allocate _images in awakeFromNib which is never called given the class is created via the singleton pattern (sharedInstance) and not loaded from a .nib file.
Therefore move the code from awakeFromNib into init and dump awakeFromNib:
BrowserController.m:
- (id)init
{
self = [super init];
if (self) {
myImageObject *p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
return self;
}
Further confusion: you have implemented an initWithFrame method. Why?
I have a class, Object2D, with the following interface:
#class Point2D;
#class Vector2D;
#interface Object2D : NSObject
{
Point2D* position;
Vector2D* vector;
ShapeType figure;
CGSize size;
}
#property (assign) CGSize size;
#property ShapeType figure;
- (Point2D*) position;
- (void)setPosition:(Point2D *)pos;
- (Vector2D*) vector;
- (void)setVector:(Vector2D *)vec;
- (id)initWithPosition:(Point2D*)pos vector:(Vector2D*)vec;
- (void)move:(CGRect)bounds;
- (void)bounce:(CGFloat)boundryNormalAngle;
- (void)draw:(CGContextRef)context;
#end
And implementation:
#implementation Object2D
#synthesize size;
#synthesize figure;
- (Point2D*) position
{
return position;
}
- (void)setPosition:(Point2D *)pos
{
if (position != nil) {
[position release];
}
position = pos;
}
- (Vector2D*) vector
{
return vector;
}
- (void)setVector:(Vector2D *)vec
{
if (vector != nil) {
[vec release];
}
vector = vec;
}
...
- (void) dealloc
{
if (position != nil) {
[position release];
}
if (vector != nil) {
[vector release];
}
}
I don't use #synthesize with position and vector because I think, I have to release them before change their values.
I have two question:
If I want to get position value, is this correct? Will I get a reference to position or a new one?
ball = [[Object2D alloc] init];
ball.position = [[Point2D alloc] initWithX:0.0 Y:0.0];
ball.vector = [[Vector2D alloc] initWithX:5.0 Y:4.0];
Point2D* pos2;
pos2 = ball.position; <-- IS THIS CORRECT?
Second question:
Do I need to release previous values before assign new ones to position and vector?
If you use...
#property (nonatomic, retain) Point2D *position;
and the same for vector it will do the retain / release for you. (and #synthesize them obviously!)
So if you do the following....
Point2D *newPosition = [[Point2D alloc] init];
[myObject setPosition:newPosition];
[newPosition release];
OR
[myObject setPosition:[[[Point2D alloc] init] autorelease]];
Then the retains / releases will be handled. You will need to add a dealloc to your Object2D class and do EITHER....
[position release];
OR
[self setPosition:nil];
both will release the object upon cleanup.
I've found an error on my code. Correct setters methods could be:
- (void)setPosition:(Point2D *)pos
{
[pos retain];
[position release];
position = pos;
}
- (void)setVector:(Vector2D *)vec
{
[vec retain];
[vector release];
vector = vec;
}
I am trying to create a class where the width and height of a 2 dimensional array can be dynamically created at the point of initialisation with init parameters.
I have been looking through the web for hours and cannot find a way.
Using a standard C syntax [][] does not allow for a variable to be used to declare the array.
The mutable arrays within Objective C, in all examples I have seen, require the objects to be hard coded at the time of creation.
Is there a way of creating a 2 dimensional array within an object with parameters to define the sizes at the point of creation?
Hoping someone can tell me what I am missing...
You can do this quite easily by writing a category on NSMutableArray:
#interface NSMutableArray (MultidimensionalAdditions)
+ (NSMutableArray *) arrayOfWidth:(NSInteger) width andHeight:(NSInteger) height;
- (id) initWithWidth:(NSInteger) width andHeight:(NSInteger) height;
#end
#implementation NSMutableArray (MultidimensionalAdditions)
+ (NSMutableArray *) arrayOfWidth:(NSInteger) width andHeight:(NSInteger) height {
return [[[self alloc] initWithWidth:width andHeight:height] autorelease];
}
- (id) initWithWidth:(NSInteger) width andHeight:(NSInteger) height {
if((self = [self initWithCapacity:height])) {
for(int i = 0; i < height; i++) {
NSMutableArray *inner = [[NSMutableArray alloc] initWithCapacity:width];
for(int j = 0; j < width; j++)
[inner addObject:[NSNull null]];
[self addObject:inner];
[inner release];
}
}
return self;
}
#end
Usage:
NSMutableArray *dynamic_md_array = [NSMutableArray arrayOfWidth:2 andHeight:2];
Or:
NSMutableArray *dynamic_md_array = [[NSMutableArray alloc] initWithWidth:2 andHeight:2];
Here is another pure Objective C Version
#import foundation.h
#interface ZTwoDimensionalArray : NSObject{
#package
NSMutableArray* _array;
int _rows, _columns;
}
-(id) initWIthRows:(int)numberOfRows andColumns:(int) numberOfColumns;
-(id) getObjectAtRow:(int) row andColumn:(int)column;
-(void) setObject:(id) anObject atRow:(int) row andColumn:(int)column;
#end
#import "ZTwoDimensionalArray.h"
#implementation ZTwoDimensionalArray
-(id) initWIthRows:(int)numberOfRows andColumns:(int) numberOfColumns{
if (self = [super init]) {
_array = [NSMutableArray initWithCapacity:numberOfRows*numberOfColumns];
_rows = numberOfRows;
_columns = numberOfColumns;
}
return self;
}
-(id) getObjectAtRow:(int) row andColumn:(int)column{
return [_array objectAtIndex: row*_rows + column];
}
-(void) setObject:(id) anObject atRow:(int) row andColumn:(int)column{
[_array insertObject:anObject atIndex:row*_rows + column];
}
-(void) dealloc{
[_array release];
}
#end
Here's another way. Of course this is just for int but the code could easily be altered for other datatypes.
AOMatrix.h:
#import <Cocoa/Cocoa.h>
#interface AOMatrix : NSObject {
#private
int* matrix_;
uint columnCount_;
uint rowCount_;
}
- (id)initWithRows:(uint)rowCount Columns:(uint)columnCount;
- (uint)rowCount;
- (uint)columnCount;
- (int)valueAtRow:(uint)rowIndex Column:(uint)columnIndex;
- (void)setValue:(int)value atRow:(uint)rowIndex Column:(uint)columnIndex;
#end
AOMatrix.m
#import "AOMatrix.h"
#define INITIAL_MATRIX_VALUE 0
#define DEFAULT_ROW_COUNT 4
#define DEFAULT_COLUMN_COUNT 4
/****************************************************************************
* BIG NOTE:
* Access values in the matrix_ by matrix_[rowIndex*columnCount+columnIndex]
****************************************************************************/
#implementation AOMatrix
- (id)init {
return [self initWithRows:DEFAULT_ROW_COUNT Columns:DEFAULT_COLUMN_COUNT];
}
- (id)initWithRows:(uint)initRowCount Columns:(uint)initColumnCount {
self = [super init];
if(self) {
rowCount_ = initRowCount;
columnCount_ = initColumnCount;
matrix_ = malloc(sizeof(int)*rowCount_*columnCount_);
uint i;
for(i = 0; i < rowCount_*columnCount_; ++i) {
matrix_[i] = INITIAL_MATRIX_VALUE;
}
// NSLog(#"matrix_ is %ux%u", rowCount_, columnCount_);
// NSLog(#"matrix_[0] is at %p", &(matrix_[0]));
// NSLog(#"matrix_[%u] is at %p", i-1, &(matrix_[i-1]));
}
return self;
}
- (void)dealloc {
free(matrix_);
[super dealloc];
}
- (uint)rowCount {
return rowCount_;
}
- (uint)columnCount {
return columnCount_;
}
- (int)valueAtRow:(uint)rowIndex Column:(uint)columnIndex {
// NSLog(#"matrix_[%u](%u,%u) is at %p with value %d", rowIndex*columnCount_+columnIndex, rowIndex, columnIndex, &(matrix_[rowIndex*columnCount_+columnIndex]), matrix_[rowIndex*columnCount+columnIndex]);
return matrix_[rowIndex*columnCount_+columnIndex];
}
- (void)setValue:(int)value atRow:(uint)rowIndex Column:(uint)columnIndex {
matrix_[rowIndex*columnCount_+columnIndex] = value;
}
#end
If I create a custom initWith for an object do I essentially include the code I would add should I want to override init?
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
}
return(self);
}
e.g.
-(id) initWithX:(int) inPosX andY:(int) inPosY {
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return(self);
}
gary
You can create one designated initializer that accepts all parameters that you want to make available in initialization.
Then you call from your other -(id)init your designated initializer with proper parameters.
Only the designated initializer will initialize super class [super init].
Example:
- (id)init
{
return [self initWithX:defaultX andY:defaultY];
}
- (id)initWithPosition:(NSPoint)position
{
return [self initWithX:position.x andY:position.y];
}
- (id)initWithX:(int)inPosX andY:(int)inPosY
{
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return self;
}
The designated initializer is -(id)initWithX:andY: and you call it from other initializers.
In case you want to extend this class you call your designated initializer from subclass.
I'd suggest creating one main initializer that handles most of the work. You can then create any number of other initializers that all call this main one. The advantage of this is if you want to change the initialization process, you'll only have to change one spot. It might look like this:
-(id) initWithX:(float)x {
if (self = [super init]) {
/* do most of initialization */
self.xVal = x;
}
return self;
}
-(id) init {
return [self initWithX:0.0f];
}
In this example initWithX: is our main initializer. The other initializer (init) simply calls initWithX: with a default value (in this case 0).
Yes, that's exactly how I do it. One slight change will cut out a line of code:
if (self = [super init]) {
As opposed to:
self = [super init];
if(self) {
For modern Objective-C ...
UDFile.h
#import <Foundation/Foundation.h>
#interface UDFile : NSObject
#property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
#end
UDFile.m
#import "UDFile.h"
#implementation UDFile
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = [name copy];
}
return self;
}
- (instancetype)init {
return [self initWithPathname:#""];
}
Sometimes, you want to reuse some initialisation code and modify the behaviour only slightly for specific initialisers. In this case, I do the following:
- (id) init
{
self = [super init];
if (!self) return nil;
// These values are always initialised this way
ivar1 = 10;
ivar2 = #"HellO";
ivar3 = [[NSMutableArray alloc] initWithCapacity:10];
ivar4 = 22;
return self;
}
- (id) initWithIvar4:(int) aValue
{
// call -init on self, which will call -init on super for us, and set
// up ivar1, ivar2, ivar3, and ivar4.
self = [self init];
if (!self) return nil;
// Change ivar4 from the default 22 to whatever aValue is.
ivar4 = aValue;
return self;
}