Class methods, saving state, a better way to register functions - objective-c

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.

Related

Objective-c Priority Queue

I've started using Objective-c for iOS programming. I switched over from Java, and I wanted to know if there were any existing libraries like the Java Collections Framework for Obj-c, more specifically a priority queue implementation. I've done some searches, but have been unable to come up with anything.
UPDATE: I found this, but would have no idea how to use it myself: http://www.ohloh.net/p/pqlib
I was unable to find an implementation of a priority queue, so I went ahead and made my own. I'm not sure how robust it is, but I hope it might point others in the right direction.
PriorityQueue.h
//
// PriorityQueue.h
//
#import <Foundation/Foundation.h>
#import "comparable.h"
//Implements a priority queue. All objects in queue must implement the comparable protocol and must be all of the same type. The queue can be explicity typed at initialization, otherwise the type of the first object entered will be the type of the queue
#interface PriorityQueue : NSObject{
NSMutableArray *queue;
Class type;
}
- (id)init;
- (id)initWithObjects:(NSSet *)objects;
- (id)initWithCapacity:(int)capacity;
- (id)initWithCapacity:(int)capacity andType:(Class)oType; //Queue will reject objects not of that type
#pragma mark - Useful information
- (BOOL)isEmpty;
- (BOOL)contains:(id<comparable, NSObject>)object;
- (Class)typeOfAllowedObjects; //Returns the type of objects allowed to be stored in the queue
- (int) size;
#pragma mark - Mutation
- (void)clear;
- (BOOL)add:(id<comparable, NSObject>)object;
- (void)remove:(id<comparable, NSObject>)object;
#pragma mark - Getting things out
- (id)peek;
- (id)poll;
- (id)objectMatchingObject:(id<comparable, NSObject>)object;
- (NSArray *)toArray;
#pragma mark -
- (void)print;
#end
PriorityQueue.m
//
// PriorityQueue.m
//
#import "PriorityQueue.h"
#define INITIAL_CAPACITY 50
#implementation PriorityQueue
#pragma mark - Initialization
- (id)init{
return [self initWithCapacity:INITIAL_CAPACITY andType:nil];
}
- (id)initWithObjects:(NSSet *)objects{
self = [self initWithCapacity:INITIAL_CAPACITY andType:nil];
for (id<comparable, NSObject>object in objects){
[self add:object];
}
return self;
}
- (id)initWithCapacity:(int)capacity{
return [self initWithCapacity:capacity andType:nil];
}
- (id)initWithCapacity:(int)capacity andType:(Class)oType{
self = [super init];
if(self){
queue = [[NSMutableArray alloc] init];
type = oType;
}
return self;
}
#pragma mark - Useful information
- (BOOL)isEmpty{
if(queue.count == 0){
return YES;
}
else{ return NO;}
}
- (BOOL)contains:(id<comparable, NSObject>)object{
//Search the array to see if the object is already there
for(id<comparable> o in queue){
if([o isEqual:object]){
return YES;
}
}
return NO;
}
- (Class)typeOfAllowedObjects{
NSLog(#"Allowed Types: %#", type);
return type;
}
- (int) size{
return [queue count];
}
#pragma mark - Mutation
//Mutation
- (void)clear{
[queue removeAllObjects];
}
//A "greater" object (compareTo returns 1) is at the end of the queue.
- (BOOL)add:(id<comparable, NSObject>)object{
//Make sure the object's type is the same as the type of the queue
if(type == nil){
// NSLog(#"Type is nil");
type = [object class];
}
if([object class] != type){
NSLog(#"ERROR: Trying to add incorrect object");
return NO;
}
if([queue count] == 0){
[queue addObject:object];
return YES;
}
for(int i = 0; i < [queue count]; i++){
if([object compareTo:queue[i]] < 0){
[queue insertObject:object atIndex:i];
return YES;
}
}
[queue addObject:object];
return YES;
}
- (void)remove:(id<comparable, NSObject>)object{
[queue removeObject:object];
}
#pragma mark - Getting things out
- (id)peek{
return queue[0];
}
- (id)poll{
//Get the object at the front
id head = queue[0];
//Remove and return that object
[queue removeObject:head];
return head;
}
- (id)objectMatchingObject:(id<comparable, NSObject>)object{
//Search the array to see if the object is already there
for(id<comparable> o in queue){
if([o isEqual:object]){
return o;
}
}
return nil;
}
- (NSArray *)toArray{
return [[NSArray alloc] initWithArray:queue];
}
#pragma mark -
- (NSString *)description{
return [NSString stringWithFormat:#"PriorityQueue: %# allows objects of type %#", queue, type];
}
- (void)print{
NSLog(#"%#", [self description]);
}
#end
Comparable.h
//
// comparable.h
//
#import <Foundation/Foundation.h>
//NOTE: Class must check to make sure it is the same class as whatever is passed in
#protocol comparable
- (int)compareTo:(id<comparable, NSObject>)object;
- (BOOL)isEqual:(id<comparable, NSObject>)object;
#end
See https://mikeash.com/pyblog/using-evil-for-good.html where Mike implements Objective-C wrapper for C++ STD priority queue.
CFBinaryHeap can be used as a Priority Queue and is described as such in the docs: https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFBinaryHeapRef/
The downsides seem to be:
1) There is no remove or update element capability. As far as I can tell you can only remove the min element.
2) It is very C-like and not very pleasant to use in Objc or Swift.
My approach supporting value updates. As CFBinaryHeap does not support updating values I put them on an invalidation list and once being extracted the object is inserted again and a new extraction is made.
/**
Objective-C wrapper around CFBinaryHeap implementing a priority queue and extended by updating a previous value
*/
NS_ASSUME_NONNULL_BEGIN
#interface BEPriorityQueue<ObjectType, ValueType> : NSObject
- (void)dispose;
#property (nonatomic, readonly) NSUInteger count;
- (void)insert:(ObjectType)object value:(ValueType)value;
- (void)update:(ObjectType)object value:(ValueType)value;
/** returns and removes object with lowest value (highest priority */
- (ObjectType)extractMinimum;
- (BOOL)containsObject:(ObjectType)object;
- (id)valueForObject:(id)object;
- (void)removeAllObjects;
#end
NS_ASSUME_NONNULL_END
With this implementation:
NS_ASSUME_NONNULL_BEGIN
#interface BEPriorityQueue()
- (CFComparisonResult)compareObject:(id)object1 with:(id)object2;
#end
static CFComparisonResult BEPriorityQueueCompareItems(const void *ptr1, const void *ptr2, void *info)
{
id object1 = (__bridge id)ptr1;
id object2 = (__bridge id)ptr2;
BEPriorityQueue* queue = (__bridge id)info;
return [queue compareObject:object1 with:object2];
}
static const void *BEPriorityQueueItemRetain(CFAllocatorRef allocator, const void *ptr) {
return CFRetain(ptr);
}
static void BEPriorityQueueItemRelease(CFAllocatorRef allocator, const void *ptr) {
CFRelease(ptr);
}
#implementation BEPriorityQueue
{
BOOL _disposed;
CFBinaryHeapRef _binaryHeapRef;
NSMapTable* _objectToValue;
NSMutableSet* _invalidated;
}
- (instancetype)init
{
self = [super init];
if (self)
{
CFBinaryHeapCallBacks callbacks = (CFBinaryHeapCallBacks) {
.version = 0,
.retain = &BEPriorityQueueItemRetain,
.release = &BEPriorityQueueItemRelease,
.copyDescription = &CFCopyDescription,
.compare = &BEPriorityQueueCompareItems
};
CFBinaryHeapCompareContext compareContext = (CFBinaryHeapCompareContext) {
.version = 0,
.info = (__bridge void *)(self),
.retain = NULL,
.release = NULL,
.copyDescription = NULL,
};
_binaryHeapRef = CFBinaryHeapCreate(NULL, 0, &callbacks, &compareContext);
_objectToValue = [NSMapTable strongToStrongObjectsMapTable];
_invalidated = [NSMutableSet set];
}
return self;
}
- (void)dealloc
{
[self dispose];
if (_binaryHeapRef != NULL)
{
CFRelease(_binaryHeapRef);
_binaryHeapRef = NULL;
}
}
- (void)dispose
{
[self removeAllObjects];
_disposed = YES;
}
#pragma mark internal
- (CFComparisonResult)compareObject:(id)object1 with:(id)object2
{
id value1 = [_objectToValue objectForKey:object1];
id value2 = [_objectToValue objectForKey:object2];
return (CFComparisonResult)[value1 compare:value2];
}
#pragma mark interface
- (NSUInteger)count
{
BEEnsureFalse(_disposed);
return (NSUInteger)CFBinaryHeapGetCount(_binaryHeapRef);
}
- (id)extractMinimum
{
BEEnsureFalse(_disposed);
const void *ptr = NULL;
if (!CFBinaryHeapGetMinimumIfPresent(_binaryHeapRef, &ptr))
return nil;
id object = (__bridge id)ptr;
id value = [_objectToValue objectForKey:object];
CFBinaryHeapRemoveMinimumValue(_binaryHeapRef);
[_objectToValue removeObjectForKey:object];
// if the objects was invalidated, it may no longer be the minimum
// therefore reinsert the object and extract again
if ([_invalidated containsObject:object])
{
[_invalidated removeObject:object];
[self insert:object value:value];
return [self extractMinimum];
}
return object;
}
- (void)insert:(id)object value:(id)value
{
BEEnsureFalse(_disposed);
BEEnsureIsNotNil(object);
BEEnsureIsNotNil(value);
BEEnsureTrue([value respondsToSelector:#selector(compare:)]); // <NSComparable>
[_objectToValue setObject:value forKey:object]; // first to be available furing insertion compare
CFBinaryHeapAddValue(_binaryHeapRef, (__bridge void *)object);
}
- (void)update:(id)object value:(id)value
{
BEEnsureFalse(_disposed);
BEEnsureIsNotNil(object);
BEEnsureTrue([value respondsToSelector:#selector(compare:)]); // <NSComparable>
[_objectToValue setObject:value forKey:object]; // first to be available during insertion compare
[_invalidated addObject:object];
}
- (BOOL)containsObject:(id)object
{
BEEnsureFalse(_disposed);
return CFBinaryHeapContainsValue(_binaryHeapRef, (__bridge void *)object);
}
- (id)valueForObject:(id)object
{
return [_objectToValue objectForKey:object];
}
- (void)removeAllObjects
{
CFBinaryHeapRemoveAllValues(_binaryHeapRef);
[_objectToValue removeAllObjects];
[_invalidated removeAllObjects];
}
#end
NS_ASSUME_NONNULL_END

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

Add open feature to Document based App

I have a Document based Application, with an TextView.
I want to add an open feature that writes it in the TextView.
I have the code, but it woundn't work.
The TextView is empty.
Heres my code:
#import "Document.h"
#implementation Document
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSFont *courier = [NSFont fontWithName: #"Courier" size:12];
[_textView setString: #"Blabla"];
[_textView setFont:courier];
NSLog(#"Tesg");
[_textView setString:#"TEST"];
}
- (id)init
{
NSLog(#"Tesg");
self = [super init];
if (self) {
// Add your subclass-specific initialization here.
}
return self;
}
- (NSString *)windowNibName
{
// Override returning the nib file name of the document
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
return #"Document";
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
// Add any code here that needs to be executed once the windowController has loaded the document's window.
}
+ (BOOL)autosavesInPlace
{
return YES;
}
/*- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
// Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
// You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
NSException *exception = [NSException exceptionWithName:#"UnimplementedMethod" reason:[NSString stringWithFormat:#"%# is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
#throw exception;
return nil;
}
*/
- (NSData *)dataOfType:(NSString *)pTypeName error:(NSError **)pOutError {
NSDictionary * zDict;
if ([pTypeName compare:#"public.plain-text"] == NSOrderedSame ) {
zDict = [NSDictionary dictionaryWithObjectsAndKeys:
NSPlainTextDocumentType,
NSDocumentTypeDocumentAttribute,nil];
} else {
NSLog(#"ERROR: dataOfType pTypeName=%#",pTypeName);
*pOutError = [NSError errorWithDomain:NSOSStatusErrorDomain
code:unimpErr
userInfo:NULL];
return NULL;
} // end if
NSString * zString = [[_textView textStorage] string];
NSData * zData = [zString dataUsingEncoding:NSASCIIStringEncoding];
return zData;
} // end dataOfType
/*
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
// Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO.
// You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
// If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.
NSLog(data);
NSException *exception = [NSException exceptionWithName:#"UnimplementedMethod" reason:[NSString stringWithFormat:#"%# is unimplemented", NSStringFromSelector(_cmd)] userInfo:nil];
#throw exception;
return YES;
}
*/
- (BOOL)readFromData:(NSData *)pData
ofType:(NSString *)pTypeName
error:(NSError **)pOutError {
if ([pTypeName compare:#"public.plain-text"] != NSOrderedSame) {
NSLog(#"** ERROR ** readFromData pTypeName=%#",pTypeName);
*pOutError = [NSError errorWithDomain:NSOSStatusErrorDomain
code:unimpErr
userInfo:NULL];
return NO;
} // end if
NSDictionary *zDict = [NSDictionary dictionaryWithObjectsAndKeys:
NSPlainTextDocumentType,
NSDocumentTypeDocumentAttribute,
nil];
NSDictionary *zDictDocAttributes;
NSError *zError = nil;
zNSAttributedStringObj =
[[NSAttributedString alloc]initWithData:pData
options:zDict
documentAttributes:&zDictDocAttributes
error:&zError];
if ( zError != NULL ) {
NSLog(#"Error readFromData: %#",[zError localizedDescription]);
return NO;
} // end if
NSString *content = [zNSAttributedStringObj string];
NSLog(#"%#", content);
NSLog(#"%c", [_textView isEditable]);
[_textView setString:content];
return YES;
} // end readFromData
#end
Thanks!
Please do not flag it as "Not a real Question" or something else.
The problem is the readXXX methods are called before the window is created. This means that _textView is nil. You need to use -(void)windowControllerDidLoadNib:(NSWindowController *)windowController to populate _textView with the information you load from the file.
You can avoid being caught out by this kind of problem in future by placing NSAssert calls in your code to confirm the preconditions required for your methods to operate correctly:
NSAssert(_textView != nil, #"_textView not initialized");

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

argument isKindOfClass: [NSNumber class] - sane way to check this?

So I was playing with something where the class type of the arg is unknown until runtime.
like this:
- (NSNumber *)doWhatever:(id)arg
{
// this ALWAYS FAILS
if ([arg isKindOfClass:[NSNumber class]]) {
return arg;
}
else {
// what was it???
NSLog("arg klass=%#", [arg class]); // prints NSCFNumber
}
// This check works correctly.
if ([arg isKindOfClass:[NSArray class]]) {
for (id x in arg) {
NSNumber *result = [self doWhatever:x];
if (result) {
return result;
}
}
}
return nil;
}
- (void)someMethod
{
NSArray *myArray = [NSArray arrayFromObjects:[NSNumber numberWithInt:3], nil]];
NSNumber *myNum = [self doWhatever:myArray];
NSLog(#"myNum=%#", myNum);
}
This is obviously a contrived example of what I'm trying to do.
The point is this never works b/c the class of "arg" always appears as NSCFNumber, and I can't figure out a way to check for that.
Any way to make it less confusing to detect whether an arbitrary value in an array is an integer or not?
UPDATE:
Thanks #chuck, #omz, and #Nikita Leonov for your help. What I posted here originally was just a simplification of the problem I was having and wrote it here without running it first. That code once updated to remove the errors (see below) runs fine actually.
The mistake I made in my real code that I was having trouble with was equally silly--I was passing the array back in to "doWhatever" instead of the item at array's index, which is why I was having problems.
Thanks for trying to help, however misguided my question was.
Sorry for wasting everybody's time!
Corrected code that runs as desired:
- (NSNumber *)doWhatever:(id)arg
{
// this NOW WORKS
if ([arg isKindOfClass:[NSNumber class]]) {
return arg;
}
else {
// what was it???
NSLog(#"arg klass=%#", [arg class]); // prints NSCFNumber
}
// This check works correctly.
if ([arg isKindOfClass:[NSArray class]]) {
for (id x in arg) {
NSNumber *result = [self doWhatever:x];
if (result) {
return result;
}
}
}
return nil;
}
- (void)someMethod
{
NSArray *myArray = [NSArray arrayWithObjects:
[NSNumber numberWithInt:1],
[NSNumber numberWithInt:2],
[NSNumber numberWithInt:3],
[NSNumber numberWithInt:4],
nil];
NSNumber *myNum = [self doWhatever:myArray];
NSLog(#"myNum=%#", myNum);
}
NSCFNumber is a subclass of NSNumber. As long as you're using isKindOfClass: rather than isMemberOfClass: or [arg class] == [NSNumber class], it should work. If not, your problem is elsewhere.