Exception: object cannot be nil - objective-c

I have this function:
+ (double)runProgram:(id)program usingVariableValues:(NSDictionary *)variableValues
{
NSMutableArray *stack = [CalculatorBrain programToStack:program];
if (variableValues) {
NSSet *variables = [CalculatorBrain variablesUsedInProgram:program];
NSUInteger index = 0;
for (id obj in [stack copy]) {
if ([variables containsObject:obj]) {
[stack replaceObjectAtIndex:index withObject:[variableValues valueForKey:obj]];
}
index++;
}
}
return [self popOperandOffStack:stack];
}
When I pass it a nil object for variableValues the program crashes and the exception raised is that I can't replaceObjectAtIndex:withObject: with a nil object. I understand why the exception is being called but shouldn't if (variableValues) prevent that whole block from even being entered if variableValues is nil?

You can have an NSDictionary thats not nil yet doesn't contain a key of obj .......

Related

CS193P assignment 2 Clear function

Given this implementation:
- (NSMutableArray *)programStack
{
if (_programStack == nil)
_programStack = [[NSMutableArray alloc]init];
return _programStack;
}
- (id)program
{
return [self.programStack copy];
}
+ (double)popOperandOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack)
[stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]]) {
result = [topOfStack doubleValue];
}
else if ([topOfStack isKindOfClass:[NSString class]])
{
NSString *operation = topOfStack;
// C
if ([operation isEqualToString:#"C"])
{
[stack removeAllObjects];
return 0;
}
}
}
Am I correct in assuming that the class method's call to [stack removeAllObjects] only affects a copy of a copy rather than removing all objects from the instance's _programStack ? How would you, from that class method, affect the instance's variable? Or is there a better way to do this?
Thanks.
[stack removeAllObjects]; will remove all objects from stack. If you call + (double)popOperandOffStack:(NSMutableArray *)stack from an object, passing an instance variable as stack, then popOperandOffStack: operates on that instance variable and will remove all objects:
[[self class] popOperandOffStack:self.myInstanceArray]
If, on the other hand, you call [[self class] popOperandOffStack:[self.myInstanceArray mutableCopy]] it will operate on a copy.

How to cast object of class id to CGPoint for NSMutableArray?

If I have an object myObject of class id, how would I "cast" it as a CGPoint (given that I have performed introspection and know myObject to a CGPoint)? This is despite the fact that CGPoint is not a real Obj-C class.
Simply doing (CGPoint)myObject returns the following error:
Used type 'CGPoint' (aka 'struct CGPoint') where arithmetic or pointer type is required
I want to do this so that I can check if the object being passed to an NSMutableArray is a CGPoint, and if it is, to wrap the CGPoint in an NSValue automatically; e.g.:
- (void)addObjectToNewMutableArray:(id)object
{
NSMutableArray *myArray = [[NSMutableArray alloc] init];
id objectToAdd = object;
if ([object isKindOfClass:[CGPoint class]]) // pseudo-code, doesn't work
{
objectToAdd = [NSValue valueWithCGPoint:object];
}
[myArray addObject:objectToAdd];
return myArray;
}
ADDITIONAL CODE
Here are the functions I use to perform "introspection":
+ (BOOL)validateObject:(id)object
{
if (object)
{
if ([object isKindOfClass:[NSValue class]])
{
NSValue *value = (NSValue *)object;
if (CGPointEqualToPoint([value CGPointValue], [value CGPointValue]))
{
return YES;
}
else
{
NSLog(#"[TEST] Invalid object: object is not CGPoint");
return NO;
}
}
else
{
NSLog(#"[TEST] Invalid object: class not allowed (%#)", [object class]);
return NO;
}
}
return YES;
}
+ (BOOL)validateArray:(NSArray *)array
{
for (id object in array)
{
if (object)
{
if ([object isKindOfClass:[NSValue class]])
{
NSValue *value = (NSValue *)object;
if (!(CGPointEqualToPoint([value CGPointValue], [value CGPointValue])))
{
NSLog(#"[TEST] Invalid object: object is not CGPoint");
return NO;
}
}
else
{
NSLog(#"[TEST] Invalid object: class not allowed (%#)", [object class]);
return NO;
}
}
}
return YES;
}
+ (NSValue *)convertObject:(CGPoint)object
{
return [NSValue valueWithCGPoint:object];
}
A CGPoint is not an Objective-C object. You cannot pass one to your addObjectToNewMutableArray: method. The compiler will not let you.
You need to wrap the CGPoint in an NSValue and pass that wrapper to your addObjectToNewMutableArray: method.
If you have an NSValue and you want to test whether it contains a CGPoint, you can ask it like this:
if (strcmp([value objCType], #encode(CGPoint)) == 0) {
CGPoint point = [value CGPointValue];
...
}
a point isnt an object and therefore cant be cast to one...
or vice-versa
casting doesnt transform data it only changes how data is interpreted!
id is basically short for NSObject* btw

NSdictionary returns error when 1 returns

Everything works when i get more then 1 objects back but when its only 1 it reacts weird, i can't find the solution for it.
First i set everything in an array:
NSArray *array = [[[dictionary objectForKey:#"Response"] objectForKey:#"objecten"] objectForKey:#"object"];
if (array == nil) {
NSLog(#"Expected 'results' array");
return;
}
then i use a for loop on a dictionary
for (NSDictionary *resultDict in array) {
SearchResult *searchResult;
NSString *wrapperType = [resultDict objectForKey:#"type"];
if ([wrapperType isEqualToString:#"rent"])
{
searchResult = [self parseHuur:resultDict];
}
if (searchResult != nil) {
[searchResults addObject:searchResult];
}}
So when results get back more then 1 everything works great, but when just one gets back i get:
-[__NSCFString objectForKey:]: unrecognized selector sent to instance 0x6e52c30
*** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSCFString objectForKey:]:
unrecognized selector sent to instance 0x6e52c30'
it points to this line:
NSString *wrapperType = [resultDict objectForKey:#"type"];
I really don't get it...
i check the results of the api in the browser with the same link and it really returns 1 object, but when i log the resultDict (NSlog it) i get only one answer: id instead of the whole object with all parameters (i don't know if this is the right name for it)
how can that be ?
Some of your results aren't full NSDictionaries but rather just NSStrings. You can check for this:
for (id result in array) {
if ([result isKindOfClass:[NSDictionary class]]) {
NSDictionary *resultDict = (NSDictionary *)result;
...
As per your comments, array is not always an array as you have mentioned. It could be an array or dictionary. So try this,
id someObject = [[[dictionary objectForKey:#"Response"] objectForKey:#"objecten"] objectForKey:#"object"]; //naming it as someObject since it is not always an array
if (someObject == nil) {
NSLog(#"Expected 'results' array");
return;
}
if ([someObject isKindOfClass:[NSDictionary class]]) { //Just add this
someObject = [NSArray arrayWithObject:someObject];
}
NSArray *array = (NSArray *)someObject;//type cast to an array now
for (NSDictionary *resultDict in array) {
SearchResult *searchResult;
NSString *wrapperType = [resultDict objectForKey:#"type"];
if ([wrapperType isEqualToString:#"rent"])
{
searchResult = [self parseHuur:resultDict];
}
if (searchResult != nil) {
[searchResults addObject:searchResult];
}
}
When you use the fast enumeration for a NSDictionary, the iterating variable is from the set of keys in the dictionary not the values.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocFastEnumeration.html
The resultDict isn't an NSDictionary hence you can't invoke objectForKey: on this object.
A better solution would be to treat resultDict as id type in the for loop, and check its class type for NSDictionary before using it.
-[__NSCFString objectForKey:]
So it's calling the objectForKey: method on an NSString. It seems that the API you're using for getting the objects follows a common idiom: it uses duck-typing/polimorphism (to use these nice OO-related words) and it returns either an array of objects if it has more than results, or a single object (and not an array of one element) when it has only one result. So, you have to use reflection (OMG, even more OO terminology!) to inspect whether the returned object is actually an array - either
id result = [dictionary objectForKey:#"Response"];
id value;
if ([result isKindOfClass:[NSDictionary class]]) {
value = [[result object objectForKey:#"objecten"] objectForKey:#"object"];
} else {
value = result;
}
or
id value;
if ([dictionary isKindOfClass:[NSDictionary class]]) {
value = [[[dictionary objectForKey:#"Result"] result object objectForKey:#"objecten"] objectForKey:#"object"];
} else {
value = dictionary;
}
Try both, whichever works should be fine.

Block with NSSet

can anyone tell me please why returnSet is returning as nil when there are lowercase characters in 'program'
I have stepped through and the NSLog is definitely picking the variables out but when it addObject: it just doesn't?
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSMutableSet *returnSet = [[NSMutableSet alloc]init];
if ([program isKindOfClass:[NSArray class]]) {
[program enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop)
{
if ([obj isKindOfClass:[NSString class]]) {
if ([obj rangeOfCharacterFromSet:[NSCharacterSet lowercaseLetterCharacterSet]].location != NSNotFound) {
NSLog(#"Variable: %#", obj);
[returnSet addObject:obj];
}
}
}];
}
return returnSet;
}
The posted code has no bug. It cannot return a value of nil.
Your error is elsewhere.
I'm guessing that your problem is an ARC memory management problem. The code you posted returns a non-owning reference to the set it creates. Unless you save it to a strong instance variable, it will be deallocated.

CALayerArray was mutated while being enumerated

I have to delete an object of an array every now and then, when I do it I get this error.
Collection < CALayerArray: 0xc4f3b20> was mutated while being enumerated
The error appears on this method, which is the accesor of the Array:
- (NSArray *)occupantsArray
{
if (dispatch_get_current_queue() == moduleQueue)
{
return occupantsArray;
}
else
{
__block NSArray *result;
dispatch_sync(moduleQueue, ^{ //ON THIS LINE
result = [occupantsArray copy];
});
return [result autorelease];
}
}
As you can see Im taking care of not returning the original array but a copy, but it still crashes.
Also the method where Im deleting elements of the array is this.
- (void)eraseJIDFromArray:(NSString*)jid{
dispatch_block_t block = ^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i = 0;
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[occupantsArray removeObjectAtIndex:i];
}
i++;
}
[pool drain];
};
if (dispatch_get_current_queue() == moduleQueue)
block();
else
dispatch_async(moduleQueue, block);
}
The array can have upto 200 elements, so it can take some time to go through all of them, but I'm setting queues, dont know what else I can do.
Any ideas?
Thanks.
This code:
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[occupantsArray removeObjectAtIndex:i];
}
i++;
}
is probably causing your problems. You shouldn't be removing elements while enumerating the array. The way you avoid this is by using an NSMutableIndexSet:
NSMutableIndexSet *indexes = [NSMutableIndexSet set]; // assuming NSInteger i;
for(NSString *jidAct in occupantsArray){
if([jidAct isEqualToString:jid]){
[indexes addIndex:i];
}
i++;
}
[occupantsArray removeObjectsAtIndexes:indexes];