I want to add an object into an NSMutableArray:
NSLog(#"Object text: %#", object.text);
NSLog(#"Object: %#", object);
[appdelegate.objects addObject:object];
NSLog(#"Objects array size: %i", [appdelegate.objects count]);
This is the output:
Object text: This is the text
Object: <Object: 0x6e762c0>
Objects array size: 0
How is this possible, I add an object, on the next line, it is still empty. The NSMutableArray is not nil, because that would raise an exception.
Anybody a guess?
It would not raise an exception if it was nil. You can still message a nil object if it usually responds to that message. In this case, you'll just get 0. I think you're not allocating the array. Make sure you're doing this:
array = [[NSMutableArray alloc] init];
As a debugging tip, if you're unsure about the state of an object and want to make sure the object indeed exists and is ready to be used, use assert(appdelegate.objects); If the array is nil, your code will stop executing at this line. If it doesn't stop at this line, then you know the object exists in memory.
Your NSMutableArray is indeed almost certainly null. It won't raise an exception, because sending any message to nil in ObjC is a no-op and would behave as you're seeing, with a return value of zero or nil, etc.
Try logging that as well to double check.
Related
From the lynda.com core data course. Simon mentioned that if the fetchedobjects array WAS empty, i.e. there are no managed objects in the store then the code would work fine and no error would be logged.
However if the fetchedobjects array was set to nil then the error would display.
But isnt the fetchedobjects array set to nil when the pointer of the array is created (a line before the if statement)?
I might be having trouble differentiating between a nil array and an empty array. Can someone untangle this for me? thankyou.
- (IBAction)fetchObjects:(id)sender {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Course" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Problem! %#",error);
}
for (Course *c in fetchedObjects) {
NSLog(#"Course: %# by author: %#", c.title, c.author);
}
}
The isn't such a thing as a nil array, the proper description is that it is an NSArray pointer that points to nil an empty array is an NSArray pointer that points to a valid NSArray object that has no members.
In the code you are talking about, fetched objects isn't set to nil, it is set to the result of the method call. If the method call fails, then it returns nil, and populates the NSError parameter, if it succeeds it returns an array, which could be empty if no results are found.
This answer does not respond to the fetchedobjects message but rather points out the differences between an empty array and a nil array (or any nil object for that matter)
Objective C will respond with nil to any message sent to a nil object. This is by design to allow code that checks against things like count == 0 just like you are doing.
Any nil object will accept any message and respond with nil. But an array that has no objects will respond correctly to other things it is sent.
If the array is nil it will not perform any actual functions and will always respond nil to anything called on it.
The best practice is to check if the object is nil beforehand to determine other paths to handle when the object is not set.
nil is an object set to nothing. nil and 0 are similar but no the same. Mathematically nil and 0 are equivalent. therefore a nil array does not return 0 from array.count, rather it returns nil. however since nil and 0 are equivalent mathematically the result returns true.
I hope that clears this up a bit.
If executeFetchRequest returns nil then you should check the value of error. Otherwise there is no error and fetchedObjects will contain an initialized NSArray that contains zero or more objects.
The line NSError *error = nil; is setting error to nil, although it's really unnecessary.
I am trying to assign objects in an NSMutableArray using this code
- (IBAction)variablePressed:(UIButton *)sender {
NSString *variable = [sender currentTitle];
if (!_variableToBePassedIntoTheDictionary) _variableToBePassedIntoTheDictionary = [[NSMutableArray alloc] init];
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable];}
but when I run this program the program breaks at the last line, since I have set the debugger to show warnings if a Exception is raised. Running the program without breakpoints, the program gives SIGARBT and crashes. I then assign these values to a dictionary which would be passed to the Model for further calculations.
- (IBAction)testVariableValues:(id)sender {
if (!_variablesAssignedInADictionary) _variablesAssignedInADictionary = [[NSMutableDictionary alloc] init];
[_variablesAssignedInADictionary setObject:_digitToBePassedIntoTheVariable forKey:_variableToBePassedIntoTheDictionary];
NSLog(#"%#", _variablesAssignedInADictionary);}
P.S. I am new in Objective C, can anybody please explain when do we use
#synthesize someProperty;
vs
#synthesize someProperty = _someProperty;
Thank You!
The first time the method is called you create the NSMutableArray and then attempt to replace an object which is not there. The reference says:
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
The index of the object to be replaced. This value must not exceed
the bounds of the array. Important Raises an NSRangeException if
index is beyond the end of the array.
And 0 will exceed the bounds of an empty array.
Try this instead:
- (IBAction)variablePressed:(UIButton *)sender
{
NSString *variable = [sender currentTitle];
if (_variableToBePassedIntoTheDictionary == nil)
{
_variableToBePassedIntoTheDictionary = [[NSMutableArray alloc] init];
[_variableToBePassedIntoTheDictionary addObject:variable];
}
else
{
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable];
}
}
Taken from the docs :
The index of the object to be replaced. This value must not exceed the
bounds of the array.
As I see from your code your array is initialized and there is no object at index 0. hence you try to replace an object at an index which is out of bounds as your array is empty.
Very simple question:
You told it stops on an exception. Fair enough. What for an exception? Let me guess, an out of bounds exception? The exception tells you what's wrong in most cases.
replaceObjectAtIndex:0 : is there something at that index or not? Probably not.
In your code you test the condition:
if(!_variableToBePassedIntoTheDictionary)
and if the condition is true, that is the array is nil, then you alloc-init it.
In the following statement:
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable];,
you try to replace the object at index 0 with variable. But in the case above, if you just alloc-init the array, it is empty and than you cannot replace the object at index 0 as not existing, and this raises an exception:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM replaceObjectAtIndex:withObject:]: index 0 beyond bounds for empty array'
So what you have to do is to change the last line as follows:
if([_variableToBePassedIntoTheDictionary count]==0) {
[_variableToBePassedIntoTheDictionary addObject:variable]
} else {
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable]
}
As far as the second question about properties, consider that the role of synthesize is to create for you the setter/getter methods based on the attributes you assigned to the #property. In the new Objective-C you don't need to declare the ivar associated to the property (the ivar is the instance variable that represent the property) and the compiler by default assigns the ivar the name of the property. By using the
#synthesize someProperty = _someProperty
convention you specify that you want the ivar to be called _someProperty. The advantage of this approach with respect to the default one is that you cannot confuse accessing to a property using the setter/getter methods and the ivar directly, that is you cannot make possible mistakes like:
someProperty=value
but instead you must write:
_someProperty=value
or
self.someProperty=value
Anyway have a look at Obj-C documentation for this, it is quite exhaustive.
I came across this method:
-(void) someMethod {
NSMutableArray *anArray = [[NSMutableArray alloc] init];
// Do stuff with anArray ...
[anArray release];
anArray = nil;
}
Is setting the array to nil necessary?
In this code by sending a release message to the array, it will causes the array to be deallocated.
In this case, it is a pointless waste of key strokes because the variable anArray goes out of scope immediately.
In other cases, where the variable stays in scope for a while after you release the object its pointing to, it is a good idea, because, if you accidentally dereference it, you will get a EXC_BAD_ACCESS which is easy to spot, and if you send a message to it, it will be ignored (except for returning nil / 0).
As others have mentioned, setting it to nil will help your code not crash if you reference the dealloced object. If you reference a dealloced you will get EXC_BAD_ACCESS error and your app will crash. Since a nil object returns nil if a message is sent to it, your app will not crash.
In the example you provide, it is not necessary to nil it out, since it is contained in a method. However, you do not want to nil out a variable if you expect to use it somewhere else in the code, since the value will then be nil.
No it is not necessary.
It is just for safe reason (to not send a message to a zombie)
And you can test if your ivar is nil or not to realloc:
[ivar release];
ivar=nil;
...
if (ivar==nil) {
ivar = [[NSObject alloc] init];
}
[ivar setValue:#"toto"];
It's not necessary but considered good behaviour to set dangling pointers to nil.
I have just started to jump into the realm of Objective-C and am slowly getting it all. I have been working on unarchiving a file that was a NSMutableArray and then initializing in my model with that array. The array is filled with various NSMutableDicationary's. From what I have seen it will add those dictionaries as non-mutable, so I went ahead and copied the regular and put them in a mutable and remove the old one. This solution seems to work for every instance except the very first.
I am at a loss as to why it would work for all but the first.
Here is how I am initializing it all
-(id) initWithList:(NSMutableArray *)savedList
{
self = [super init];
if (self)
{
int size=0;
serverList=[[NSMutableArray alloc] initWithArray:savedList copyItems:YES];
size=[serverList count];
for(int i=0;i<size;i++)
{
loginList=[NSMutableDictionary dictionaryWithDictionary:[serverList objectAtIndex:i]];
[serverList addObject:loginList];
[serverList removeObjectAtIndex:i];
}
}
return self;
}
Here is the code that is throwing the error, The value is being read off of a checkbox in a tableview and passed here to change the value.
-(void)setMount:(int)row value:(NSNumber*)boolAsNumber
{
[[serverList objectAtIndex:row] setObject:boolAsNumber forKey:#"mountshare"];
}
Here is the error that it shows when I try and change the first element
2010-12-01 13:38:54.445 Network Share[35992:a0f] *** -[NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
Thanks for your help. If there is a better way please let me know.
This loop code is wrong:
size=[serverList count];
for(int i=0;i<size;i++)
{
loginList=[NSMutableDictionary dictionaryWithDictionary:[serverList objectAtIndex:i]];
[serverList addObject:loginList];
[serverList removeObjectAtIndex:i];
}
When you remove an object, the array is renumbered. After you've processed the 1st object at index 0, the original 2nd object is becoming the 1st object at index 0, but i is now set to index 1, which is where the original 3rd object is! This means you're only processing alternate items from the original array, and the 2nd, 4th, etc items never get swapped, and that's why you get the errors you're seeing.
One way to solve this would be to replace the "i" in the objectAtIndex: and removeObjectAtIndex: calls with "0", so you're always taking items off the front of the array.
The alternate solution would be to create a separate newServerList array and insert your new objects into that. At the end of the loop, release the old serverList and set the variable to point to newServerList.
Your indexes are messed up. As soon as you remove the object at index 0, the next one will take it's place and you will never replace that, because you then carry on with index 1.
{immutable0, immutable1}
i = 0:
addObject:
{immutable0, immutable1, mutable0}
removeObjectAtIndex:
{immutable1, mutable0}
i = 1:
addObject:
{immutable0, mutable0, mutable02}
removeObjectAtIndex:
{immutable0, mutable02}
--> still got the immutable there. Remember to never remove objects from a mutable array you are looping through at the same time.
You could condense the code a bit:
NSMutableArray *serverList = [NSMutableArray arrayWithCapacity:[savedList count]];
for (NSDictionary *dictionary in savedList)
{
mutable = [dictionary mutableCopy];
[serverList addObject:mutable];
[mutable release];
}
Unrelated to your problem: the argument is obviously wrong (NSMutableArray), if you expect an immutable array there; and if you create your serverList that way, there is no need for a deep copy (copyItems:YES).
I am getting a EXC_BAD_ACCESS (SIGBUS) on this line in my iPhone project:
if (timeoutTimer) [timeoutTimer invalidate];
The thing that has me stumped is that I don't understand how that line could crash, since the if statement is meant to be checking for nil. Am I misunderstanding the way Objective-C works, or do line numbers in crash statements sometime have the wrong line in them?
Just because a variable is set to a value other than nil doesn't mean it's pointing to a valid object. For example:
id object = [[NSObject alloc] init];
[object release];
NSLog(#"%#", object); // Not nil, but a deallocated object,
// meaning a likely crash
Your timer has probably already been gotten rid of (or possibly hasn't been created at all?) but the variable wasn't set to nil.
I just ran into a similar issue, so here's another example of what might cause a check such as yours to fail.
In my case, I was getting the value from a dictionary like this:
NSString *text = [dict objectForKey:#"text"];
Later on, I was using the variable like this:
if (text) {
// do something with "text"
}
This resulted in a EXC_BAD_ACCESS error and program crash.
The problem was that my dictionary used NSNull values in cases where an object had an empty value (it had been deserialized from JSON), since NSDictionary cannot hold nil values. I ended up working around it like this:
NSString *text = [dict objectForKey:#"text"];
if ([[NSNull null] isEqual:text]) {
text = nil;
}
They should be the same. Perhaps the line number is in fact incorrect.
Look for other possible errors near that in your code and see if you find anything.