I have the below code in my app which mallocs memory to a struct object. This method is called several times in the code, each time mallocing memory for a new Vector3D object.
What I want to do is capture the pointer to the malloced ShapeClass object (a C struct) so that I can later free each object in my dealloc method.
Can anyone advise me how I can add the pointer to an array ?
Thank you.
vectorCount = [floatArray.array count] / 3;
ShapeClass *vectorArray = malloc(sizeof(Vector3D) * vectorCount);
for (int i=0; i<vectorCount; i++) {
vectorArray[i].x = [[floatArray.array objectAtIndex:(i*3)] floatValue];
vectorArray[i].y = [[floatArray.array objectAtIndex:(i*3)+1] floatValue];
vectorArray[i].z = [[floatArray.array objectAtIndex:(i*3)+2] floatValue];
}
return vectorArray;
// I WANT TO INSERT SOMETHING HERE TO CAPTURE THE POINTER TO vectorArray - E.G ADD IT TO AN ARRAY ?
}
}
return NULL;
}
Use [NSValue valueWithPointer:] to store it in the container classes.
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
SomeObject *object = [[SomeObject alloc] init];
//Store the pointer to object
[dictionary setValue:[NSValue valueWithPointer:object] forKey:#"object"];
Related
I have NSMutableArray of NSMutableArrays:
NSMutableArray *array = [[NSMutableArray alloc]init];
for (int i = 0; i < 5; i++)
{
NSMutableArray *miniArray = [[NSMutableArray alloc]init];
for (int k = 0; k < 30; k++)
{
[miniArray addObject:#"0"];
}
[array addObject:miniArray];
}
Then, when I try to do this:
[[array objectAtIndex:packIndex]replaceObjectAtIndex:index withObject:#"1"];
it crashes with: [__NSCFArray replaceObjectAtIndex:withObject:]: mutating method sent to immutable object'
Why ? How to fix ? Thanks !
UPD:
I save this array in NSUserDefaults:
[defaults setObject:array forKey:#"mainArray"];
Then, I read it in the other class:
array = [[NSMutableArray alloc]initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"mainArray"]];
Also, I must to mention that sometimes the code runs well and it changes "0" to "1". But it also crashes sometimes. So I cant see the logic, why it works fine or why it crashes sometimes.
The problem is that when you read the array out of NSUserDefaults, the mini-arrays are not automatically NSMutableArrays.
Try this:
array = [[NSMutableArray alloc]initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"mainArray"]];
for(int i = 0; i < array.count; i++) {
NSArray * tempArray = array[i];
array[i] = [tempArray mutableCopy];
}
Edit:
Best Coder's answer explains why this is.
Objects stored in NSUserDefaults are stored as immutable versions, basically NSUserDefaults is a plist and there is no flag marking an array as mutable/immutable so when you read them back out, they are assumed to be immutable.
Values returned from NSUserDefaults are immutable, even if you set a
mutable object as the value. For example, if you set a mutable string
as the value for "MyStringDefault", the string you later retrieve
using stringForKey: will be immutable.
Instead, make a mutableCopy of the array you retrieve from NSUserDefaults, add your object, then set your new array back in.
see this link:
https://developer.apple.com/documentation/foundation/nsuserdefaults
Try using more verbose code:
NSMutableArray *tempArray = [array objectAtIndex:packIndex];
[tempArray replaceObjectAtIndex:index withObject:#"1"];
I want to get the index of an object within the NSMutableArray of categories.
The category object has an attribute "category_title" and I want to be able to get the index by passing the value of category_title.
I have looked through the docs and can't find a simple way to go about this.
NSArray does not guarantee that you can only store one copy of a given object, so you have to make sure that you handle that yourself (or use NSOrderedSet).
That said, there are a couple approaches here. If your category objects implement isEqual: to match category_title, then you can just use -indexOfObject:.
If you can't do that (because the category objects use a different definition of equality), use -indexOfObjectPassingTest:. It takes a block in which you can do whatever test you want to define your "test" - in this case, testing category_title string equality.
Note that these are all declared for NSArray, so you won't see them if you are only looking at the NSMutableArray header/documentation.
EDIT: Code sample. This assumes objects of class CASCategory with an NSString property categoryTitle (I can't bring myself to put underscores in an ivar name :-):
CASCategory *cat1 = [[CASCategory alloc] init];
[cat1 setCategoryTitle:#"foo"];
CASCategory *cat2 = [[CASCategory alloc] init];
[cat2 setCategoryTitle:#"bar"];
CASCategory *cat3 = [[CASCategory alloc] init];
[cat3 setCategoryTitle:#"baz"];
NSMutableArray *array = [NSMutableArray arrayWithObjects:cat1, cat2, cat3, nil];
[cat1 release];
[cat2 release];
[cat3 release];
NSUInteger barIndex = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if ([[(CASCategory *)obj categoryTitle] isEqualToString:#"bar"]) {
*stop = YES;
return YES;
}
return NO;
}];
if (barIndex != NSNotFound) {
NSLog(#"The title of category at index %lu is %#", barIndex, [[array objectAtIndex:barIndex] categoryTitle]);
}
else {
NSLog(#"Not found");
}
Not sure that I understand the question but something like this might work (assuming the Mutable Array contains objects of Class "Category"):
int indx;
bool chk;
for (Category *aCategory in theArray)
{
chk = ([[aCategory category_title] isEqualToString:#"valOfCategoryTitle"])
if ( chk )
indx = [theArray indexOfObject:aCategory];
}
Try this code much more simpler:-
int f = [yourArray indexOfObject:#"yourString"];
Im using dictionary to save some positions with value "1";
/* * * header * * */
NSDictionary *Mypoints;
/* * * * * * * * */
-(void)myfunc: (float)y :(float)x{
NSLog(#"%#",Mypoints);
NSNumber *one = [NSNumber numberWithInt:1];
NSString *key = [[NSString alloc] initWithFormat:#"%f,%f",y,x];
[Mypoints setObject:one forKey:key];
//show the value
NSNumber *tempNum = [Mypoints objectForKey:key];
int i = tempNum.intValue;
NSLog(#"Value: %i",i);
//print all
NSLog(#"%#",Mypoints);
}
The first time that I call this function everything is okay, it creates the dictionary and print the "array" in last line. But when I enter this function again, it crashes with no error.
I don't know what may be happening...
I solved the crash doing it:
Mypoints = [[NSMutableDictionary dictionaryWithObject:one forKey:key]retain];
I solved how to add more points:
//into viewDidLoad
Mypoints = [[NSMutableDictionary alloc] init];
//into myfunc
[Mypoints setObject:one forKey:key];
Mypoints is not getting retained. So the first time it will be nil, the second time Mypoints will be pointing to memory which was freed.
Other minor issues in this code:
Mypoints should be set to nil before use (I assume this is done elsewhere).
Mypoints = [NSDictionary dictionaryWithObject:one forKey:key]; Your dictionary will also only contain 1 key/value pair because you are creating a new dictionary every time.
NSString *key = [[NSString alloc] initWithFormat:#"%f,%f",y,x]; This will leak, as it is not being released.
// Not keen on this being a global but whatever... It needs to be initialised somewhere in this example.
NSMutableDictionary* Mypoints = nil;
-(void)myfunc: (float)y :(float)x
{
// This is hacky but shows the principle.
if ( Mypoints == nil )
{
Mypoints = [ NSMutableDictionary alloc ] initWithCapacity: 10 ];
// Or whatever you think the size might be. Don't forget to release it somewhere.
}
NSNumber *one = [NSNumber numberWithInt:1];
NSString *key = [NSString stringWithFormat:#"%f,%f",y,x];
// This stops the leak since it is now already autoreleased.
[ MyPoints setObject: one forKey: key ]; // Be careful no other key can have the same x and y value.
//show the value
NSNumber *tempNum = [Mypoints objectForKey:key];
int i = tempNum.intValue;
NSLog(#"Value: %i",i);
//print all
NSLog(#"%#",Mypoints);
}
Because [NSDictionary dictionaryWithObject:one forKey:key] returns an autoreleased object, which gets released sometime between invocations of -myfunc:. The crash is obviously happening on the first line with NSLog().
I have an NSMutableArray which only lasts during the session.
Currently I create it like this
NSMutableArray *temp = [[NSMutableArray alloc] initWithCapacity:10];
[self setScoreArray:temp];
[temp release];
Problem is when I go to check each index I'm getting an array outofbounds error
NSNumber *previousScore = [[self scoreArray] objectAtIndex:[self quizNum]];
if ( previousScore != nil )
{
[self clearQuizBtns];
NSInteger previousScoreValue = [previousScore integerValue];
[self selectButtonAtTag:previousScoreValue];
}else {
[self clearQuizBtns];
}
I've read in other posts that initWithCapacity doesn't actually create the array. So what can I populate the array with initially?
Thanks in advance.
Two ways:
first: to initiate array with default values of NSNull class
NSMutableArray *temp = [[NSMutableArray alloc] initWithCapacity:10];
for (int i = 0 ; i < 10 ; i++)
{
[temp insertObject:[NSNull null] atIndex:i];
}
[self setScoreArray:temp];
[temp release];
and then to check: if object is kind of NSNull class means it was a never set before
id previousScore = [[self scoreArray] objectAtIndex:[self quizNum]];
if (![previousScore isKindOfClass:[NSNull class]])
{
[self clearQuizBtns];
NSInteger previousScoreValue = [(NSNumber *)previousScore integerValue];
[self selectButtonAtTag:previousScoreValue];
}else {
[self clearQuizBtns];
}
second: store scores in NSMutableDictionary and use NSNumber's as keys
// scoreDictionary property of NSMutableDictionary class must be declared in self
NSNumber *previousScore = [self.scoreDictionary objectForKey:[NSNumber numberWithInt:[self quizNum]]];
if (previousScore != nil)
{
[self clearQuizBtns];
NSInteger previousScoreValue = [previousScore integerValue];
[self selectButtonAtTag:previousScoreValue];
}else {
[self clearQuizBtns];
}
NSArray does not support "holes". The capacity is just a hint to the initializer.
You could either fill the array with placeholder objects or, more typically, change your algorithm to either fully prepopulate the array or to lazy load it linearly.
Your problem seems to be that you're never actually setting any score in the score array.. are you? NSArrays have an actual count of items in them, and accessing an index beyond that count will blow up, as you've seen. If there will only ever be a fixed (small) number of scores, like 10, then you could set them all initially to something default like:
for (int i = 0; i < 10; i++) {
[temp addObject:[NSNumber numberWithInt:0]];
}
P.S. -initWithCapacity does "create the array", it just doesn't create any objects in the array. The capacity is a hint only.
Using the arrayWithObject: or arrayWithObjects: methods can provide an array with pre-populated values.
One cool thing about NSMutableArrays is that you can just do an "init" and the array will handle adding and removing objects on the fly. Remember that you generally addObject: or removeObjectAtIndex: when dealing with mutable arrays.
In Instruments the tempPlayer object is showing a leak. In this code, in every for loop I keep on allocating a new tempPlayer instance, set its playerCode variable with a string and add it to an NSMutableArray in each iteration. Instruments shows me a leak in the alloc statement. Is there any way to prevent tat leak in the tempPlayer object ?
for(int i = 0 ; i < [homeLineupArray count] ; i++) {
NSArray * tildeSeperator = [[homeLineupArray objectAtIndex:i] componentsSeparatedByString:#"~"];
[self.tempPlayer release];
self.tempPlayer = [[LineUpsPlayer alloc] init];
tempPlayer.playerCode = [tildeSeperator objectAtIndex:0];
[matchLineUp.homeTeamPlayingEleven addObject:tempPlayer ];
}
Thanks
Harikant Jammi
I would simply do this.
for(int i = 0 ; i < [homeLineupArray count] ; i++) {
NSArray *tildeSeperator = [[homeLineupArray objectAtIndex:i] componentsSeparatedByString:#"~"];
LineUpsPLayer *player = [[[LineUpsPlayer alloc] init] autorelease];
player.playerCode = [tildeSeperator objectAtIndex:0];
[matchLineUp.homeTeamPlayingEleven addObject:player];
}
You can also replace your loop with this:
for (NSString *lineup in homeLineupArray) {
NSArray *tildeSeparator = [lineup componentsSeparatedByString:#"~"];
...
}
You don't usually want to save each item while iterating through an array to an instance variable since it keeps changing and you only reference it in the method.
Rather than using the property, tempPlayer, use a local variable, and release after adding to the array:
for(int i = 0 ; i < [homeLineupArray count] ; i++) {
NSArray * tildeSeperator = [[homeLineupArray objectAtIndex:i] componentsSeparatedByString:#"~"];
LineUpsPlayer* tempPlayer = [[LineUpsPlayer alloc] init];
tempPlayer.playerCode = [tildeSeperator objectAtIndex:0];
[matchLineUp.homeTeamPlayingEleven addObject:tempPlayer ];
[tempPlayer release];
}
The array retains the objects you add - that's why you need to release the temporary object too.
Things may depend on how do you declare your tempPlayer property in your class (and as it looks that it is temporary object, consider do you need a property accessor for it?)
for(int i = 0 ; i < [homeLineupArray count] ; i++) {
NSArray * tildeSeperator = [[homeLineupArray objectAtIndex:i] componentsSeparatedByString:#"~"];
[self.tempPlayer release]; // Decrease retain count
self.tempPlayer = [[LineUpsPlayer alloc] init]; // retain count increase by 1 or 2
tempPlayer.playerCode = [tildeSeperator objectAtIndex:0];
[matchLineUp.homeTeamPlayingEleven addObject:tempPlayer]; // retain count increase
}
So as you can see you retain your object more times then you release it so it eventually leaks. Probably your code may be rewritten this way:
for(int i = 0 ; i < [homeLineupArray count] ; i++) {
NSArray * tildeSeperator = [[homeLineupArray objectAtIndex:i] componentsSeparatedByString:#"~"];
LineUpsPlayer* tempPlayer = [[LineUpsPlayer alloc] init]; // object's retain count is 1
tempPlayer.playerCode = [tildeSeperator objectAtIndex:0];
[matchLineUp.homeTeamPlayingEleven addObject:tempPlayer]; // container takes ownership of the object
[tempPlayer release]; // we do not need to own this object as it is in container now
}
As said above, I normally use a local variable to populate the mutable array. In other cases Instruments didnt show me any leak. So is this the right way to go about ?
for(int i =0 ; i < ([arrayOfHomePlayers count]); i++ ) //creating home players detials object
{
localString = [NSMutableString string];
localString = [arrayOfHomePlayers objectAtIndex:i] ;
NSArray * localPlayersArray = [localString componentsSeparatedByString:#"~"] ;
localPlayerPosition = [ [PlayerPosition alloc] init] ;
NSArray * playerNameArray = [[localPlayersArray objectAtIndex:0] componentsSeparatedByString:#" "] ;
localPlayerPosition.globalID = [localPlayersArray objectAtIndex:1];
[(teamFormationDetails.homePlayerList) addObject:localPlayerPosition] ;
[localPlayerPosition release] ;
First, you could change the release to an autorelease and move it to the line after the self is assigned.
Second, the addobject is adding it to a collection which retains it, and I don't see you removing it from that collection, so that's your 'leak'. However, I'm not sure there even is a leak, if you intended to leave it in the collection.