Purpose of "nil' at the end of Initializing Array in Objective-C - objective-c

I have seen and done initialization of arrays and all put "nil" at the end of initialization but never question, why it is required to put there?
Plus if you are initializing your array in a loop, is it still necessary to put nil at the end of array? for example.
array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < 10; i++)
{
[array addObject:#"1"];
}
// now this line is required or not after i exit the loop?
[array addObject:nil];

This concept is called nil-termination, and it's purpose is to provide a sentinel to the receiving function or method of where the variable argument list ends.

Related

Adding a NSDictionary to a NSArray duplicates the first NSDictionary every time

So I pulled some JSON data from a web service which is stored in an NSArray called _infoFromJSON. Each array element in _infoFromJSON essentially has a dictionary of key/value pairs. The goal is to add them to myVehicleObject which is an NSMutableArray
for (NSDictionary* myDictionary in _infoFromJSON) {
myVehicleObject *vehicleInMembersProfile;
vehicleInMembersProfile = [[myVehicleObject alloc] init];
vehicleInMembersProfile.make = [[_infoFromJSON objectAtIndex:carCount] objectForKey:#"make"];
vehicleInMembersProfile.carName = [[_infoFromJSON objectAtIndex:carCount] objectForKey:#"nickname"];
vehicleInMembersProfile.year = [[_infoFromJSON objectAtIndex:carCount] objectForKey:#"year"];
carCount ++;
[self.myVehicleObject addObject:vehicleInMembersProfile] ;
};
With the above code I sort of achieved it, however it keeps adding the same 1st dictionary to myVehicleObject, so it inserts the same NSDictionary 4 times In the past I have used this:
[self.myVehicleObject addObject:[vehicleInMembersProfile copy]] ;
When I do it it throws the following exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[myVehicleObject copyWithZone:]: unrecognized selector sent to instance 0xab4e130'
What am I doing wrong here? Thanks!
Update Requested Sample JSON:
{
color = SILVER;
engine = "";
id = "CF270B81-3821-4585-8C90-7089D7A8654E";
imageUrl = "https://www.someprotectedurl.com/somefile.png";
licensePlate = "ABC-123";
make = Honda;
model = "CR-V";
nickname = "My CR-V";
vin = "XX551234687687687654";
year = 2009;
}
This may or may not be related to the problem, but your forin loop is completely broken. Whether or not your incorrect usage is the actual cause of the problem, this is something you should definitely fix as it can do nothing but cause problems.
If you want to use indexOfObject: to grab an object at an index of the array you're iterating through, you should use a regular for loop:
for (int carCount=0; index < [_infoFromJSON count]; ++carCount) {
// loop body remains identical to what you have...
// except remove carCount++;
}
But for what you're doing, a forin loop is indeed better, and forin loops can be faster since they can be handled in batches. But if you're using a forin loop, use the object you're defining in the loop declaration:
for(NSDictionary* myDictionary in _infoFromJSON) {
// myDictionary is a reference to an object in _infoFromJSON,
//for whatever given index it is currently working on
myVehicleObject *vehicleInMembersProfile = [[myVehicleObject alloc] init];
vehicleInMembersProfile.make = myDictionary[#"make"];
vehicleInMembersProfile.carName = myDictionary[#"nickname"];
vehicleInMembersProfile.year = myDictionary[#"year"];
[self.myVehicleObject addObject:vehicleInMembersProfile];
}
The error is that your class does not implement NSCopying.
However, there isn't any need to copy the object here. You're creating a new object each time through the loop. If you insert a copy, you're just pointlessly throwing away the original each time. The more likely cause if you're seeing odd loop behavior is that your loop is mixing up different kinds of enumeration, as pointed out by nhgrif. Instead of accessing [_infoFromJSON objectAtIndex:carCount], just use myDictionary.
I would probably use a for loop rather than a foreach since you need the array index, I would code it like this
NSMutableArray *vehiclesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < [_infoFromJSON count]; i++)
{
//create vehicle
myVehicleObject *vehicleInMembersProfile = [[myVehicleObject alloc] init];
vehicleInMembersProfile.make = _infoFromJSON[i][#"make"];
vehicleInMembersProfile.carName = _infoFromJSON[i][#"nickname"];
vehicleInMembersProfile.year = _infoFromJSON[i][#"year"];
//finally add the vehicle to the array
[vehiclesArray addObject:vehicleInMembersProfile] ;
}
I wasn't sure what myVehicleObject was , and why is it the same type you are adding to itself, so I changed it to an array. But you get the point.

Storing objects in an array in objective c

I'm trying to store 25 objects in an array
for (int iy=0; iy<5; iy++) {
for (int ix=0; ix<5; ix++) {
TerrainHex *myObject = [[TerrainHex alloc] initWithName:(#"grassHex instance 10000") width:mGameWidth height:mGameHeight indexX:ix indexY:iy];
myObject.myImage.y += 100;
[TerrainHexArray addObject:myObject];
[self addChild:(id)myObject.myImage];
}
}
NSLog(#"Terrain array: %u", [TerrainHexArray count]);
The log is coming back as zero though.
In the .h file I have
#property NSMutableArray *TerrainHexArray;
And in the .m file I have..
#synthesize TerrainHexArray;
I just tried what someone suggested below, which is..
NSMutableArray *TerrainHexArray = [[NSMutableArray] alloc] init];
But it's just giving me a warning saying expected identifier.
It's almost certain that TerrainHexArray does not exist when you're doing the addObject calls and the NSLog. You say you tried adding the alloc/init after someone suggested it, which indicates you don't understand object management in Objective-C.
I'd suggest you step back, find a book on Objective-C, and read at least the first few chapters (up through the discussion of alloc/init et al) before you attempt any more coding.
Incidentally, it's standard C++/Objective-C coding practice (except in Microsoft) to use identifiers with a leading lower case character for instance names, reserving leading caps for types/class names.
What is TerrainHexArray? It looks like a class name, not an instance of an array. If you create a mutable array, then you can add the items to the array.
NSMutableArray *hexArray = [[NSMutableArray] alloc] init];
for (int iy=0; iy<5; iy++) {
for (int ix=0; ix<5; ix++) {
TerrainHex *myObject = [[TerrainHex alloc] initWithName:(#"grassHex instance 10000") width:mGameWidth height:mGameHeight indexX:ix indexY:iy];
myObject.myImage.y += 100;
[hexArray addObject:myObject];
[self addChild:(id)myObject.myImage];
}
}
NSLog(#"Terrain array: %u", [hexArray count]);

objective-c changing array contents inside for loop

I have an issue that (I think) might have to do with scope, but I'm not sure. I'm trying to do something that I think should be simple, but I am getting a strange result, and I could truly use some advice. I would say I'm an early-objective-c programmer, but not a complete newb.
I have written a function in objective-c that I would like to use to change the key-names in a mutable array of mutable dictionary objects. So, I want to pass in a mutable array of mutable dictionary objects, and return the same mutable array with the same dictionary objects, but with some of the key-names changed. Make sense?
I have tried several log statements in this code, which seem to indicate that everything I'm doing is working, except when the for loop is finished executing (when I try to test the values in the temp array), the array appears to contain only the LAST element in the source array, repeated [source count] times. Normally, this would lead me to believe I'm not writing the new values correctly, or not reading them correctly, or even that my NSLog statements aren't showing me what I think they are. But might this be because of scope? Does the array not retain its changes outside of the for loop?
I have put a fair amount of time into this function, and I have exhausted my bag of tricks. Can anyone help out?
-(NSMutableArray *)renameKeysIn:(NSMutableArray*)source {
/*
// Pre:
// The source array is an array of dictionary items.
// This method renames some of the keys in the dictionary elements, to make sorting easier later.
// - "source" is input, method returns a mutable array
*/
// copy of the source array
NSMutableArray *temp = [source mutableCopy];
// a temporary dictionary object:
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
// These arrays are the old field names and the new names
NSMutableArray *originalField = [NSMutableArray arrayWithObjects:#"text", #"created_at",nil];
NSMutableArray *replacedField = [NSMutableArray arrayWithObjects:#"title", #"pubDate", nil];
// loop through the whole array
for (int x =0; x<[temp count]; x++) {
// set the temp dictionary to current element
[dict setDictionary:[temp objectAtIndex:x]];
// loop through the number of keys (fields) we want to replace (created_at, text)... defined in the "originalField" array
for (int i=0; i<[originalField count]; i++)
{
// look through the NSDictionary item (fields in the key list)
// if a key name in the dictionary matches one of the ones to be replaced, then replace it with the new one
if ([dict objectForKey:[originalField objectAtIndex:i]] != nil) {
// add a new key/val pair: the new key *name*, and the old key *value*
[dict setObject:[dict objectForKey:[originalField objectAtIndex:i]]
forKey:[replacedField objectAtIndex:i]];
// remove the old key/value pair
[dict removeObjectForKey:[originalField objectAtIndex:i]];
}// end if dictionary item not null
}// end loop through keys (created_at, text)
[temp replaceObjectAtIndex:x withObject:dict];
}// end loop through array
// check array contents
for (int a=0; a<[temp count]; a++){
NSLog(#"Temp contents: ############ %#",[[temp objectAtIndex:a] objectForKey:#"pubDate"]);
}
return temp;
} // end METHOD
I think the issue is on the line with:
[dict setDictionary:[temp objectAtIndex:x]];
Since these things are almost all working in pointers (instead of copying contents), every element of your temp array will point to the dict dictionary, which is set to be whatever the latest key's dictionary is. I think setting the actual pointer will fix the issue.
dict = [temp objectAtIndex:x];

object inside NSMutableArray is not update immediately after updating

I have a very strange error using NSMutableArray in cocos2d/xcode
In my code, I have a NSMutableArray containing object A
NSMutableArray *peasantArray;
and another NSMutableArray contain object B
NSMutableArray *treeArray;
in the scheduled update function, I called another function which is essentially the following functionality:
I would loop through all the object A inside the *peasantArray, and if any peasant object has a variable var == GameEntityCommandIdling, I would modify the object B in the second NSMutableArray *treeArray,
The issue is that I notice sometimes after I modified the object A inside the peasantArray, the modified variable (var) is being modified/updated inside the object A after by printing out the variable status in a scheduled fashion; but if I am to loop through the NSMutableArray *peasantArray again in the next schedule (1/30s), I will again find the object A with the older/un-updated variable (var), and this is causing my algorithm to be wrong,
However, if I loop through the NSMutableArray *peasantArray less than 1second, each time I would see the variable (var) for object A correctly as the updated variable value,
So is there a limit on how fast I can iterate over the NSMutableArray?
here are some piece of code that I basically just mentioned,
NSMutableArray *peasantArray;
NSMutableArray *treeArray;
.....
peasantArray = [[[NSMutableArray alloc] initWithCapacity:1]retain];
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
....
//inside the update()
for (int i = 0;i < [peasantArray count];i++)
{
Peasant *A = [peasantArray objectAtIndex:i];
if (A.status == something)
{
printf("A.status is something\n");
A.status = sometingelse;
...
//update other things not related to this NSMutableArray or object
}
}
.....
SO the error is that sometimes I will have multiple printf of "A.status is something", although only one of it should be printed,
I really appreciate any help,
Thanks,
So is there a limit on how fast I can iterate over the NSMutableArray?
Definitely no. That would be the stupidest implementation of an array I'd ever encountered. :)
The simplest explanation is usually the answer. You say the message is printed more than once. Well have you checked that maybe more than one Peasant has its status set to something?
If that's not the case, are you certain the status update is printed twice in the same frame, and not twice in two subsequent updates?
Earlier on you seemed to indicate that the effect of iterating over one array and modifying the other array's objects is invalidated somehow. That made me want to point out that if you have the same object in both arrays, modifying the object's properties in array A will also modify the properties of the same object contained in array B.
You may want to give breakpoints a(nother) try. The problem should be easy to locate when you step through the code.
Here you have a memory leak
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
}
you should release A after adding it to the array since the addObject adds a reference count to the object.
for(int i = 0; i < 1; i++)
{
Peasant *A = [[Peasant alloc] init];
[peasantArray addObject:A];
[A release];
}

Need help with NSMutableArray solution

Im currently devising a solution for my object BuildingNode *tower which is held inside NSMutableArray *gameObjects, to attack EnemyNode *enemy objects also held inside the gameObjects array.
I have a proposed solution which occasionally causes my game to freeze (temporarily) as the solution employed is quite buggy.
My solution is that each tower object contains its own NSMutableArray *targets which is synthesized from the Tower Class. If an enemy comes into range or out of range of any given tower object, the corrosponding index of the EnemyNode *enemy object from the gameObjects array is saved as an NSNumber into the targets array, or alternatively if the enemy object is out of range, it is removed from the .targets array.
Basically the idea is that the targets array holds the indices of any enemy that is in scope of the tower.
The problem I seem to be facing is that because this tower.targets array is updated dynamically all the time, i believe that if i'm doing some sort of operation on a particular index of tower.targets that then is removed, i get this error:
-[BuildingNode distance]: unrecognized selector sent to instance 0x2dd140
Each BuildingNode *tower has different attacking alogrithms that use the tower.targets array for calling back the sorted/desired enemy.
For example, a random attack style will randomize a number between 0 & [tower.targets count] then I can create a pointer to gameObjects with the corresponding [tower.targets intValue].
Something like this:
EnemyNode *enemy = (EnemyNode *)[gameObjects objectAtIndex:[[tower.targets objectAtIndex:rand]intValue]];
So this will find a random enemy from the potential .targets array and then create a pointer object to an enemy.
I've put in many if statements to ensure that in the case of a .targets index being removed mid-sorting, the operation shouldnt go ahead, thus removing the game failure rate, but it still occurs occassionally.
Heres my code:
Please note that BuildingNode *tower == BuildingNode *build.
This is just a snippet of the iteration inside gameObjects.
//Potential Enemies (Indices) Add and Delete/
if (enemy.distance < build.atk_distance && !enemy.isExploding){
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if(![build.targets containsObject:index]){
[build.targets addObject:index];
}
}
else {
NSNumber *index = [NSNumber numberWithInt:[array indexOfObject:enemy]];
if ([build.targets containsObject:index]){
[build.targets removeObject:index];
}
}
}
//Aiming// Nearest Algorithm.
//Will find the nearest enemy to the building
if (enemy.distance < build.atk_distance){
if (!build.isAttacking || build.atk_id == 0){
if ([build.targets count]){
if ([build.atk_style isEqualToString:#"near"]){
int l_dist;
for (int i = 0; i < [build.targets count]; i++){
//Grab the Enemy from Targets
if ([build.targets objectAtIndex:i]){
if([array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]){
EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]];
if (temp){
int c_dist = temp.distance;
if (!l_dist || c_dist < l_dist){
l_dist = c_dist;
build.atk_id = temp.uniqueID;
build.isAttacking = YES;
}
}
}
}
}
}
}}
Unrecognized selector sent = calling a method on an object that doesn't exist. In this case, you're calling the distance method/property getter on a BuildingNode object.
The only distance I see is on your temp object, which is retrieved by EnemyNode *temp = [array objectAtIndex:[[build.targets objectAtIndex:i]intValue]]; That indicates you are really pulling a BuildingNode object out of the array instead of an EnemyNode. Find out why that happens.