iPhone Application crashes - iphone

When I do the following my application crashes and I get an error (terminate called after throwing an instance of 'NSException') whenever i run the simulation:
for (...)
[Array1 replaceObjectAtIndex:i withObject: [NSString stringWithFormat:#" Trip %i",i+1]]
OK after writing the problem I have found that the error is "0 is beyond bounds for empty array".

You really really really need to post the type of exception and your code to give us a reasonable chance of solving your problem. However, I'm going to taker a shot at it anyway.
My guess is your code looks something like this:
Array1 = [[NSMutableArray alloc] initWithCapacity: someNumber];
for (i = 0 ; i < someNumber ; ++i)
{
[Array1 replaceObjectAtIndex:i withObject: [NSString stringWithFormat:#" Trip %i",i+1]];
}
All arrays start out with 0 objects in them even if you use -initWithCapacity:. That method only provides a hint to the runtime that the array will grow to the specified size at some point. You need to use -addObject:.
Edit
Yep, from your edit, I'm sure I am right. The array has started out empty and you are trying to replace the object at index 0 which isn't there yet.
The code above should be changed as follows:
array1 = [[NSMutableArray alloc] initWithCapacity: someNumber]; // fixed naming convention too :-)
for (i = 0 ; i < someNumber ; ++i)
{
[array1 addObject: [NSString stringWithFormat:#" Trip %i",i+1]];
}
-addObject: adds the new objects to the end of the array.
If you want something that looks more like a C or Java array, you can prefill the array with NSNull objects
array1 = [[NSMutableArray alloc] initWithCapacity: 6]; // fixed naming convention too :-)
for (i = 0 ; i < 6 ; ++i)
{
[array1 addObject: [NSNull null]];
}
// Now the following will work
[array1 replaceObjectAtIndex: 4 withObject: #"foo"];

if you are using replaceObjectAtIndex method with NSArray array type object, there is possibility of getting exception (crashed).
Use NSMutableArray for managing a modifiable array of objects.
Although replaceObjectAtIndex can also be raised the following Exception for Object and Index
Index: Raises an NSRangeException if index is beyond the end of the array.
Object: Raises an NSInvalidArgumentException if Object you are passing is nil.

Ok, here is a guess (I can't do better without more information):
You cannot change an object you are iterating over.
(Bad) Example:
for (NSObject *obj in Array1){
[Array1 replaceObjectAtIndex:i withObject: [NSString stringWithFormat:#" Trip %i",i+1]]
}

Related

NSMutableArray of NSMutableArrays. Mutating method sent to immutable object

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"];

Array1 is added into Array2 then Array1 is cleared and suddenly disappears from Array2

I have this curious thinf happening here where tempArray is supposed to be added to the masterArray each time the if statement occurs. (The if statement works perfectly and gets triggered when it should.)
I also need to clear the temp array each time it does.
My final result should then be a masterarray which contains each array it has added.(meaning all the temp arrays that I have added throughout the for loop)
Instead, it only keeps adding the tempArray to the MasterArray at index 0 every single time.
Code:
- (void)alpabetize:(NSArray *)arr {
self.tempArray = [[NSMutableArray alloc] init];
self.masterArray = [[NSMutableArray alloc] init];
for (int i = 0; i<[arr count]; i++) {
NSString *currentString = [NSString stringWithFormat:#"%#", [[[arr objectAtIndex:i] valueForKey:#"first_name"] substringToIndex:1]];
NSString *nextString = [NSString stringWithFormat:#"%#", [[[arr objectAtIndex:(i+1)] valueForKey:#"first_name"] substringToIndex:1]];
[self.tempArray addObject:[arr objectAtIndex:i]];
if (![currentString isEqualToString:nextString]) {
[self.masterArray addObject:tempArray];
[self.tempArray removeAllObjects];
}
}
}
If you remove all objects from the original array previously added to some other array. Does this also clear the other array? This makes no sense to me. There must be something wrong in my code. Can somebody help me spot my error?
Thank you for your time!
Use
[self.masterArray addObject:[tempArray copy]]; // retained copy of array
or
[self.masterArray addObject:[NSArray arrayWithArray:tempArray]]; // auto released copy of array
instead of
[self.masterArray addObject:tempArray];
In your code you are adding the reference of the same array which you are clearing in next statement.
[self.masterArray addObjectsFromArray:tempArray];
[tempArray removeAllObjects];
Are you sure you want to release the self.tempArray inside the loop? Once it enters your IF-condition the the array will be nil and you'll have no use of it unless you call the this method again. Try releasing the self.tempArray after the for loop.

Why is the retainCount is 0 instead of 1?

Here is the code. I have tested with NSString, and it return -1. But for NSMutableArray is 0 instead.
NSMutableArray *items = [[NSMutableArray alloc] init];
for(int i=0;i<10;i++)
{
[items addObject:[Possession randomPossession]];
}
/*for(int i=0;i<[items count];i++)
{
NSLog(#"%#", [items objectAtIndex:i]);
}*/
//Using Fast Enumeration
/*for(items in items)
{
NSLog(#"%#", items);
}*/
NSLog(#"%d", [items retainCount]);
I made mistake by using array in iteration. The correct one should be like this.
//Using Fast Enumeration
for(Possession *item in items)
{
NSLog(#"%#", item);
}
And the retainCount is back to normal as expected 1.
A) You should never look at retainCount, even for debugging. It's an internal implementation detail that can provide confusing results.
B) This:
for(items in items)
is almost certainly not what you mean -- you're using the array object ("items") as the iterator too! Something like:
for(Possession *item in items)
would make more sense.
update:
You might want to look at some of the examples in Apple's documentation on fast enumeration. You really want to have a separate variable that acts as your loop iterator. What you're doing (for(items in items)) sets items to each object in the array in turn, and when you exit the loop, items will be nil!

Restrict Duplicate entry in NSArray

I have an array, which contains some duplicate entries.
Firstly, is there any way to restrict duplicate entries when data getting inserted?
Secondly, if an array already having duplicate values than in some other way, we can retrieve only unique values from that array, I heard about NSSet about this, but I have no idea how to use it.
Don't use an NSSet.
You can only insert elements upon creation and cannot change the elements contained after you have created it.
If you want to add and remove objects on the fly, you can use an NSMutableSet.
Here is a demo of how to use it both NSSet and NSMutableSet, then converting the NSSet back to an NSArray (incase you want to do that):
- (void) NSMutableSetPrintTest
{
NSMutableSet *mutableSet = [[NSMutableSet alloc] init];
NSLog(#"Adding 5 objects (3 are duplicates) to NSMutableSet");
NSString *firstString = #"Hello World";
[mutableSet addObject:firstString];
[mutableSet addObject:#"Hello World"];
[mutableSet addObject:#"Goodbye World"];
[mutableSet addObject:#"Goodbye World"];
[mutableSet addObject:#"Goodbye World"];
NSLog(#"NSMutableSet now contains %d objects:", [mutableSet count]);
int j = 0;
for (NSString *string in mutableSet) {
NSLog(#"%d: %# <%p>", j, string, string);
j++;
}
NSLog(#"Now, if we are done adding and removing things (and only want to check what is in the Set) we should convert to an NSSet for increased performance.");
NSSet *immutableSet = [NSSet setWithSet:mutableSet];
NSLog(#"NSSet now contains %d objects:", [immutableSet count]);
int i = 0;
for (NSString *string in immutableSet) {
NSLog(#"%d: %# <%p>", i, string, string);
i++;
}
[mutableSet release]; mutableSet = nil;
NSLog(#"Now, if we are done with the sets, we can convert them back to an NSArray:");
NSArray *array = [immutableSet allObjects];
NSLog(#"NSArray contains %d objects", [array count]);
int k = 0;
for (NSString *string in array) {
NSLog(#"%d: %# <%p>", k, string, string);
k++;
}
}
NSMutableSet is probably the most logical thing to use.
However, be warned that a set does not maintain order of its elements (since a set, by definition, is unordered).
If that's a problem for you, then you have a couple of options:
duplicate set functionality with an NSMutableArray by invoking containsObject: before every call to addObject: (doable, but potentially slow, since arrays have O(n) search time)
use another object.
If you go with the second option, I would recommend taking a look at the excellent CHDataStructures framework, which has a subclass of NSMutableSet called CHOrderedSet, which is a set that maintains insertion order. (And since it's a subclass, it has the exact same API as an NSMutableSet)
If you've heard about NSSet, did you read the documentation? The API is similar to NSArray and very straightforward. Just like NSArray vs. NSMutableArray, you would use NSMutableSet if you need on the fly membership tests.

NSMutable Array

I have a NSMutableArray:
NSMutableArray *temp = //get list from somewhere.
Now there is one method objectAtIndex which returns the object at specified index.
What I want to do is that, I want to first check whether an object at specified index exists or not. If it exists than I want to fetch that object. Something like:
if ([temp objectAtIndex:2] != nil)
{
//fetch the object
}
But I get exception at the if statement saying that index beyond bound.
Please anyone tell me how to achieve this.
you cannot have 'empty' slots in an NSArray. If [myArray count]==2 ie array has two elements then you know for sure that there is an object at index 0 and an object at index 1. This is always the case.
Check the length first using the count method.
if ([temp count] > indexIWantToFetch)
id object = [temp objectAtIndex:indexIWantToFetch];
you could do this way:
When you initialize, do something like:
NSMutableArray *YourObjectArray = [[NSMutableArray alloc] init];
for(int index = 0; index < desiredLength; index++)
{
[YourObjectArray addObject:[NSNull null]];
}
Then when you want to add but check if it already exists, do something like this:
YourObject *object = [YourObjectArray objectAtIndex:index];
if ((NSNull *) object == [NSNull null])
{
/// TODO get your object here..
[YourObjectArray replaceObjectAtIndex:index withObject:object];
}
Just check that the index is >= 0 and < count
Returns the number of objects currently in the receiver.
- (NSUInteger)count
int arrayEntryCount = [temp count];
First of all you check the length of array-
NSMutableArray *temp = //get list from somewhere.
now check-
if(temp length)
{
Your objectclass *obj = [temp objectAtIndex:indexnumber];
// indexnumber is 0,1,2 ,3 or anyone...
}