How to cast object of class id to CGPoint for NSMutableArray? - objective-c

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

Related

Using method parameters check to see if array object is equal to another string (objective-C)?

I'm all self taught so please keep the techincal jargin to a minimum. Theoretically if I had a method and it had a parameter that is the name of an array. How do I check to see if that array index 5 is equal to #"Yes" or #"No". I know it's one of these because its testing to see if the picture is appearing in the veiw controller. Here is an example:
-(void)methodName :(NSMutableArray *)arrayNameInMethod {
if ( [NSMutableArray *(arrayNameInMethod) indexOfObject:5] == #"Yes"){
//Hide a different picture assocciated with the Array
} else {
//Unhide a different picture assocciated with the Array
};
Also how do you do use the parameter "arrayNameInMethod" to replace the object. Basically:
if(Picture Clicked and picture is Unhidden) {
[NSMutableArray *(differentArrayNameInMethod) replaceObjectAtIndex:5 withObject: #"True)
};
(this is all in another method)
Comment #2: You can't use the parameters the same way because it's a string. You can't access an array with a name as a string.
Thank you so much!
I think what you will need is a dictionary, mapping the name to the array. I will give a barebones implementation:
#interface YourClass()
{
NSMutableDictionary *_arrayMap;
}
#end
#interface YourClass
- (instancetype)init
{
self = [super init];
if (self) {
// You might do this somewhere else, like viewDidLoad:
_arrayMap = [NSMutableDictionary new];
}
return self;
}
- (void)someMethod
{
// Some functionality to add a new array:
// Note the array contains NSNumber and NSString objects:
_arrayMap[#"sampleName"] = [#[ #(0), #"one", #(2.0), #"three", #(4), #(YES)] mutableCopy];
}
-(BOOL)checkForConditionInArrayNamed:(NSString *)arrayName
{
BOOL retval = NO;
NSMutableArray *array = _arrayMap[arrayName];
if ([array count] > 5) {
id obj = array[5];
if ([obj isKindOfClass:[NSNumber class]])
retval = [obj boolValue];
}
return retval
};
#end
You then call checkForConditionInArrayNamed: to check for the condition of the named array and act accordingly.

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.

Problems on NSArray's -valueForKey: when its item is NSDictionary

I have an array which contains items of NSDictionary, I want to transform the items to other objects, my first thought is valueForKey:, so I add a category method toMyObject for NSDictionary, and call for:
[array valueForKey:#"toMyObject"]
But it doesn't work as expect, it just returns the array of NSNulls.
Any ideas to solve this problem if I don't want to enumerate the array?
Answer to myself. The valueForKey: of dictionary overwrite the default behavior, if the dictionary doesn't have the key, it will return nil and not call the accessor method as NSObject do, as Apple document says:
If key does not start with “#”, invokes objectForKey:. If key does
start with “#”, strips the “#” and invokes [super valueForKey:] with
the rest of the key.
Since NSDictionary is a cluster class, it's not recommend to subclass to overwrite the behavior. Instead I use the method swiss like this:
#implementation NSDictionary (MyAddition)
static void swizzle(Class c, SEL orig, SEL new)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
+ (void)initialize
{
if (self == [NSDictionary class]){
swizzle([NSDictionary class],
#selector(valueForKey:),
#selector(myValueForKey:));
}
}
- (id)toMyObject
{
return toMyObject;
}
...
- (id)myValueForKey:(NSString *)key
{
// for collection operators
if ([key compare:#"#" options:0 range:NSMakeRange(0, 1)] == NSOrderedSame)
return [super valueForKey:key];
if ([key isEqualToString:#"toMyObject"])
return [self toMyObject];
return [self myValueForKey:key];
}
Now it's safe for an NSArray to call valueForKey:#"toMyObject".
One more implementation without swizzling:
#implementation NSObject (MLWValueForKey)
- (id)mlw_valueForKey:(NSString *)key {
if ([key hasPrefix:#"#"]) {
return [self valueForKey:key];
}
NSAssert(![key containsString:#":"], #"Key should be selector without arguments");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [self performSelector:NSSelectorFromString(key)];
#pragma clang diagnostic pop
}
#end
#implementation NSArray (MLWValueForKey)
- (id)mlw_valueForKey:(NSString *)key {
if ([key hasPrefix:#"#"]) {
return [self valueForKey:key];
}
NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count];
for (id object in self) {
[array addObject:[object mlw_valueForKey:key]];
}
return array;
}
#end

Class methods, saving state, a better way to register functions

I'm working on an assignment which involves making an RPN calculator. I am currently using class methods to check whether a string is an operation as below:
+ (NSSet *) noOpOperations {
return [NSSet setWithObjects:#"π", nil];
}
+ (NSSet *) unaryOperations {
return [NSSet setWithObjects:#"sin",#"cos",#"log",#"+/-", nil];
}
+ (NSSet *) binaryOperations {
return [NSSet setWithObjects:#"+",#"-",#"*",#"/", nil];
}
+ (NSSet *) operations {
/* surely there is a better way - is it possible to save this call and reuse it? Or what about having the objects register themselves so you can add operations more easily? */
return [[self noOpOperations] setByAddingObjectsFromSet:
[[self unaryOperations] setByAddingObjectsFromSet:
[self binaryOperations]]];
}
+ (BOOL) isOperation:operand {
return [[self operations] containsObject:operand];
}
I believe it would be better to implement a kind of function registry system to allow dynamic adding of operations from another location in the project, but I think it would require a class variable. Is there a better way to do this than how I am doing it now?
My Personal solution for situations like this:
#define INT_OBJ(x) [NSNumber numberWithInt:x]
#implementation MyClass
static NSDictionary *operations;
enum {
kOperationNoOp = 1,
kOperationUnaryOp,
kOperationBinaryOp,
};
+(void) initialize
{
operations = [NSDictionary dictionaryWithObjectsAndKeys:#"π", INT_OBJ(kOperationNoOp),
// unary operations
#"sin", INT_OBJ(kOperationUnaryOp),
#"cos", INT_OBJ(kOperationUnaryOp),
#"log", INT_OBJ(kOperationUnaryOp),
#"+/-", INT_OBJ(kOperationUnaryOp),
// binary operations
#"+", INT_OBJ(kOperationBinaryOp),
#"-", INT_OBJ(kOperationBinaryOp),
#"*", INT_OBJ(kOperationBinaryOp),
#"/", INT_OBJ(kOperationBinaryOp), nil];
}
-(BOOL) isNoOpOperation:(NSString *) arg
{
return [[operations objectForKey:arg] intValue] == kOperationNoOp;
}
-(BOOL) isUnaryOperation:(NSString *) arg
{
return [[operations objectForKey:arg] intValue] == kOperationUnaryOp;
}
-(BOOL) isBinaryOperation:(NSString *) arg
{
return [[operations objectForKey:arg] intValue] == kOperationBinaryOp;
}
-(BOOL) isAnOperation:(NSString *) arg
{
// if objectForKey: returns nil, intValue will return 0, telling us that the input is not an operation
return [[operations objectForKey:arg] intValue] != 0;
}
#end
I find it to be very straightforward and easy to extend.

Exception: object cannot be nil

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 .......