Still have some difficulties to understand Obj-C's gestion of memory and some of its concepts.
So my main class contains a NSMutableArray containing some characters and their list of weapons. It's like this :
In Main class.h
#property (nonatomic, retain) NSMutableArray *players;
In Main class.m's init
for(int i = 0 ; i < 30 ; i++)
{
[players addObject:[[PlayerInGame alloc] init:[self.tabPlayers objectAtIndex:i] :[self.tabWeapons:objectAtIndex:i]]];
}
PlayerInGame.h
#property (nonatomic, retain) NSMutableArray *weaponsList;
PlayerInGame.m
- (id) init : (Player*) player : (Weapon*) weapon
{
[self.weaponsList addObject:weapon];
// Then I try NSLog of weaponsList's count.
}
Here weaponsList is always empty. What is wrong?
The other answers are right. On any other language if you reference a unallocated object you will get a NullPointerException. But in objective C the method sent to nil just returns 0 and it won't crash the app.. If you want further read, read this
That is why
[self.weaponsList addObject:weapon];
didn't crash, while in java if you try to add object to a unallocated array your program will crash.. As other answers pointed out, the statement
self.weaponsList = [[[NSMutableArray alloc] init] autorelease];
alloc memory to store array, and a reference is given back to to variable weaponList. Now weaponList != nil.
You have to alloc your array before add any object in it. you can use following code in viewDidLoad method
NSMutableArray *players = [[NSMutableArray allo]init];
NSMutableArray weaponsList = [[[NSMutableArray alloc] init]
I've not seen weaponList object allocation. Do you initialize it?
self.weaponsList = [[[NSMutableArray alloc] init] autorelease];
PS: Minor advice. Method "- (id) init : (Player*) player : (Weapon*) weapon" signature will look better and being used easier if you change it as
- (id) initWithPlayer:(Player *)player weapon:(Weapon *)weapon
I aslo suggest to change a bit your init syntax and init the array with object:
- (id) initWithPlayer:(Player *)aPlayer weapon:(Weapon *)aWeapon {
self.weaponsList = [NSMutableArray arrayWithObject:aWeapon];
[self.weaponsList retain];
// remember to release inside dealloc, or use autorelease
}
Related
I have this implementation file with aNSArray object userIDs
NSArray *userIDs;
NSInteger friendID;
#implementation TableViewController
-(void)reciveFriendsIDs:(NSArray *)array
{
userIDs = [NSArray arrayWithArray:array];
}
-(NSString *)getFriendId
{
return [userIDs objectAtIndex:friendID];
}
.
.
.
#end
and the method -(NSString *)getFriendId call it from another class like this :
TableViewController *tableController = [[TableViewController alloc]init];
NSString *fid = [tableController getFriendId];
But I am having an error said "-[__NSArrayI respondsToSelector:]: message sent to deallocated instance 0x20320200" and the compiler indicate the error in this line:
return [userIDs objectAtIndex:friendID];
You are allocating the NSArray with arrayWithArray static method.
In this way it's getting added in the auto release pool and the retain count will be 0.
Either retain it or manually alloc it with [[NSArray alloc] init]
I was getting the same exception on line
if(self.arrTypes != nil)
cause of the following line being used at a different place in code
[self.arrTypes release];
and replacing this code with
self.arrTypes = nil;
resolved the issue.
Best related discussions I’ve turned up: Making an array of Objects in Objective-C.. Scanned through links that came up when I prepped this post.
Self-study mode, creating mini-apps to reinforce and extend ideas I get working on book tutorials.
…………….
Goal: To store some number of objects (example class ‘Trip’) in a mutable array, contained in another object (example class ‘TourCompany’).
(portions of the two different class interface files)
#interface Trip : NSObject
#property (strong, nonatomic) NSString *travelToLocation;
……
#interface TourCompany : NSObject
#property (strong, nonatomic) NSMutableArray *trips ;
…… CODE AND GOAL IN PIECES....
1) Demonstrate that I created 3 trip objects and can access the travelToLocation ivar
....
Trip *trip0 = [[Trip alloc] init];
Trip *trip1 = [[Trip alloc] init];
Trip *trip2 = [[Trip alloc] init];
[trip0 setTravelToLocation:#"Vienna"];
[trip1 setTravelToLocation:#"Mt. St. Helens"];
[trip2 setTravelToLocation:#"Tenochtitlan"];
NSString *somePlace1 = trip1.travelToLocation;
NSLog(#" %# %#, \n \t Let's LOOK at one particular trip ivar %#.", twoBlanks, nuLn, somePlace1);
// CONSOLE RESULTS
// Let's LOOK at one particular trip ivar Mt. St. Helens.
....
2) Demonstrate that I
i) can store these 3 trip objects in a locally declared NSMutable Array
ii) can access the travelToLocation ivar
....
// put trip objects into a LOCALLY DECLARED mutable array
NSMutableArray *localTripsArray = [NSMutableArray array ];
//[myArray addObject: someOtherPerson]; - compare to a STackOverflow discussion
[localTripsArray addObject:trip0];
[localTripsArray addObject:trip1];
[localTripsArray addObject:trip2];
for (Trip *t in localTripsArray) {
NSLog(#"\n \t ^^^Trip location in LOCAL TRIPS Mutable ARRAY is %#", t.travelToLocation );
}
/*
CONSOLE RESULTS
objectInMutableArrayMiniAppChallenge[521:f803]
^^^Trip location in LOCAL TRIPS Mutable ARRAY is Vienna
2012-07-05 13:47:17.172 objectInMutableArrayMiniAppChallenge[521:f803]
^^^Trip location in LOCAL TRIPS Mutable ARRAY is Mt. St. Helens
2012-07-05 13:47:17.172 objectInMutableArrayMiniAppChallenge[521:f803]
^^^Trip location in LOCAL TRIPS Mutable ARRAY is Tenochtitlan
*/
....
3) BUT I cannot figure out why I am not storing these 3 objects an NSMutable Array ('trips') in an instance of my TourCompany class ('friendlySkiesTourCo')
...
// NOW instantiate an object in my TourCompany class
TourCompany *friendlySkiesTourCo = [[TourCompany alloc] init];
// put those same objects into the trips ivar in that object
//[myArray addObject: someOtherPerson];
// - compare to a STackOverflow discussion
//only difference I see is that instead of locally declared 'myArray'
// I'm using an ivar that's a member of an object
// I know there's are values in those objects, because I displayed them above
[friendlySkiesTourCo.trips addObject:trip0];
[friendlySkiesTourCo.trips addObject:trip1];
[friendlySkiesTourCo.trips addObject:trip2];
for (Trip *t in friendlySkiesTourCo.trips) {
NSLog(#"\n \t +++ Trip location in 'trips' mutable array in the object 'friendlySkiesTourCo', a TourCompany class object, is %#", t.travelToLocation );
}
/* Console NON-Results
I never get any output from this NSLog directive
I'll bet there is something pretty basic I don't understand
No doubt, it's in the Gol Durned Manual online , but I'm not seeing it
*/
....
4. Let's just double check that the problem is possibly a setting type problem
....
// I don't think I'm really putting the objects into this particular mutable array in the friendlySkies object correctly
// Let's just check
int howManyTripsInMutableArray = [friendlySkiesTourCo.trips count];
NSLog(#"\n \tThere are %d trips for the friendlySkiesTour Co ", howManyTripsInMutableArray);
/*
Sure enough...
2012-07-05 13:53:53.235 objectInMutableArrayMiniAppChallenge[555:f803]
There are 0 trips for the friendlySkiesTour Co
*/
Thanks for any clues. I hope that I did this writeup succinctly and that you don't think I just didn't RTGDM (that's 'gol durn' in case you wondered), because I sure tried to.
Laurel
I'm willing to bet you didn't initialize friendlySkiesTourCo.trips. ie
friendlySkiesTourCo.trips = [[NSMutableArray alloc] init];
EDIT
As #Chuck pointed out below in the comments, this allocation should really be done in the initializer method for the class:
#implementation TourCompany
-(id) init
{
self = [super init];
self.trips = [[NSMutableArray alloc] init];
return self;
}
Does this help?
trips = [NSMutableArray arrayWithCapacity:3];
trip *trip1 = [[trip alloc] init];
trip *trip2 = [[trip alloc] init];
trip *trip3 = [[trip alloc] init];
trip1.traveltolocation = #"vienna";
[trips addObject:trip1];
[trips addObject:trip2];
[trips addObject:trip3];
When I compile with the analyzer, I get a couple of messages. I have these properties declared:
#property (nonatomic, retain) SyncServicePrimary *syncAndCartOne;
#property (nonatomic, retain) SyncServiceSecondary *syncAndCartTwo;
This method is called from applicationDidBecomeActive and I get "Potential leak of an object allocated".
-(void)makeTheCartObjectsForCountry:(NSString*)country_key{
self.syncAndCartOne = [[SyncServicePrimary alloc] init];
self.syncAndCartTwo = [[SyncServiceSecondary alloc] init];
}
This is called in applicationWillResignActive; here I get "Incorrect decrement of the reference count of an object".
-(void) removeTheCartObjects{
[self.syncAndCartOne release];
self.syncAndCartOne = Nil;
[self.syncAndCartTwo release];
self.syncAndCartTwo = Nil;
}
If I set the objects to autorelease, the error goes away, but I want the objects to be released when the app hides itself.
Is this something I am doing right but that is split too far for the analyzer to see the start and end, or is this something I can do better/properly so it won't complain?
Its more than likely that I am missing a simple concept with regard to release and alloc cycles (I've come from PHP and C#).
Your problem is here:
-(void)makeTheCartObjectsForCountry:(NSString*)country_key{
self.syncAndCartOne = [[SyncServicePrimary alloc] init];
self.syncAndCartTwo = [[SyncServiceSecondary alloc] init];
}
You're creating the objects and then retaining them (because of the property declaration), so they have a reference count of 2, when only one object is referencing them.
You should do it like this:
-(void)makeTheCartObjectsForCountry:(NSString*)country_key{
SyncServicePrimary *primary = [[SyncServicePrimary alloc] init];
self.syncAndCartOne = primary;
[primary release];
SyncServiceSecondary *secondary = [[SyncServiceSecondary alloc] init];
self.syncAndCartTwo = secondary;
[secondary release];
}
You have defined the properties with attribute retain, so the analyzer assumes that the setter method for the property looks like this:
- (void)setSyncAndCartOne:(SyncServicePrimary *)newValue
{
[newValue retain];
[self->_syncAndCartOne release]; // access the instance variable holding the property value
self->_syncAndCartOne = newValue;
}
If you use #synthesize, the setter method will look like that.
So, when makeTheCartObjectsForCountry: returns, the object in syncAndCartOne has a retain count of 2, but should only have a retain count of 1. That's why using autorelease fixes it.
You shouldn't be doing [self.syncAndCartOne release] for the same reason. The setter method will send the old object a release when you assign nil to the property.
As a relative Objective-C beginner, I'm obviously still not grasping certain memory management rules. I can't figure out how to make this not crash:
#interface MyClass { NSArray *playerArray4th; }
- (void) viewDidLoad { playerArray4th = [self getAudioPlayersForSoundFile:#"rimshot" ofType:#"aif"]; }
- (NSArray*) getAudioPlayersForSoundFile:(NSString*)soundFileName ofType:(NSString*)soundFileType {
//code instantiating objects....
NSArray *toRet = [[NSArray alloc] initWithObjects:toRetTickPlayer,toRetTickPlayerCopy,toRetTickPlayerCopy2,toRetTickPlayerCopy3, nil];
return toRet;
}
Then later, in a different function:
NSArray *currentArray = playerArray4th;
[currentArray release];
currentArray = nil;
currentArray = [self getAudioPlayersForSoundFile:fileName ofType:ofType];
And it crashes when trying to access the array again:
- (void) playSound:(NSString*)soundType {
AVAudioPlayer *currentPlayer;
if ([soundType isEqualToString:#"4th"]) {
if (playerArray4thCounter >= [playerArray4th count]) playerArray4thCounter = 0;
NSLog(#"Playing from array: %#",playerArray4th);
currentPlayer = [playerArray4th objectAtIndex:playerArray4thCounter];
playerArray4thCounter++;
}
}
Try to learn about properties and about using getters and setters. Don't take shortcuts unless you know exactly what's going on.
So define the playerArray4th property in your header file:
#property (nonatomic,retain) NSArray *playerArray4th;
And then in your .m file create getter/setter:
#synthesize playerArray4th;
Then, always use self.playerArray4th for assigning and getting the variable. The prior objects will be released when needed.
So this will not leak:
self.playerArray4th = [NSArray arrayWithObjects:#"text",#"text",nil];
self.playerArray4th = [NSArray arrayWithObjects:#"new array",#"text",nil];
because the second assignment releases the first array.
Furthermore, read about using autorelease. In short, if you alloc, copy or new, you should either release or autorelease. There's a lot to read about this here on SO which I will not repeat here now.
Don't forget to put self.playerArray4th = nil; in your dealloc method.
I have been struggling with the best way of creating, accessing and updating values from a dynamic boolean array for more than a week now.
#interface myDelegate : NSObject
{
NSMutableArray *aShowNote;
}
#property (nonatomic, copy) NSMutableArray *aShowNote;
This is how I have initialised my array:
NSMutableArray *aShow = [[NSMutableArray alloc] init];
for (i=0; i < c; i++)
[aShow addObject:[NSNumber numberWithBool:false]];
self.aShowNote = aShow;
This seems to work OK but I'm baffled why each element is initialised with the same address.
But then what I've discovered in my research so far is that is seems that you need to replace the object if you want to change its value:
myDelegate *appDelegate = (myDelegate *)[[UIApplication sharedApplication] delegate];
NSInteger recordIndex = 1;
NSNumber *myBoolNo = [appDelegate.aShowNote objectAtIndex:recordIndex];
BOOL showNote = ![myBoolNo boolValue];
[appDelegate.aShowNote replaceObjectAtIndex:recordIndex withObject:[NSNumber numberWithBool:showNote]];
but this approach just seems to be over complicated (and it crashes too).
Terminating app due to uncaught exception'NSInvalidArgumentException', reason: '-[__NSArrayI replaceObjectAtIndex:withObject:]: unrecognized selector sent to instance 0x5b51d00
Any pointers to improve this code (and of course to make it work) would be very gratefully received.
Thanks
Iphaaw
the problem is that copy in a property copies the assigned object. And copy creates immutable objects.
Change your property to read: #property (nonatomic, retain) NSMutableArray *aShowNote;
And I think there is not much to improve, from what I know this is the way to go if you want an NSArray with booleans.
Why not use plain C for this simple case?
BOOL *aShow = malloc(sizeof(BOOL)*c);
for (i=0 ; i<c ; i++)
aShow[i] = false;
You just have to remember to free(aShow) when you are done with it.
It is not possible to change value of a NSNumber. It not mutable class.
Then, when you ask for two same value, the same object is return.
In your array init, why you don't initialized directly the array to avoid copy problem:
aShowNote = [[NSMutableArray alloc] init];
for (i=0; i < c; i++) {
[aShowNote addObject:[NSNumber numberWithBool:false]];
}
I'm baffled why each element is initialised with the same address.
Why? NSNumbers are immutable. The runtime only needs one NSNumber object to represent FALSE.