Question about isKindOfClass - objective-c

Please look at this code snipped
- (SoapRequest*)AddFlyData:(id)_target
action:(SEL)_action
sessionid:(int)sessionid
datasets:(FlyNetArrayOfDataSet*)datasets
{
if ([datasets isKindOfClass:[FlyNetArrayOfDataSet class]]) {
NSLog(#"Yeah");
} else {
NSLog(#"Not Yeah");
}
}
Why, when i look on my console, I get
2011-09-06 23:08:00.917 soap-test[2133:207] Not Yeah
I'm a beginner and I'm completely confused .. :s When I look in the Debugger, the variable type is SoapArray (who is the parent class of FlyNetArrayOfDataSet).
I used a method from SoapArray to initiate my instance of 'datasets', that means the class is automatically defined as Soap and not as FlyNetArrayOfDataSet ?!
Thank you
EDIT: I made a mistake, it's not NSArray but it inherits from SoapArray
This is the header file of the class FlyNetArrayOfDataSet
#import "Soap.h"
#interface FlyNetArrayOfDataSet : SoapArray
{
}
+ (NSMutableString*) serialize: (NSArray*) array;
#end
But that didn't explain me why isKindOfClass returns false ..
EDIT2: Ok I have the response of my question..
I used this method to initialize my instance
FlyNetArrayOfDataSet * arr = [FlyNetArrayOfDataSet arrayWithObject:data];
This is a static method of the superclass SoapArray that create an instance of SoapArray (Helper) .. but not an instance of FlyNetArrayOfDataSet (!)
Look at its implementation :
+ (id)arrayWithObjects:(id)firstObj, ...{
SoapArray* a = [SoapArray array];
id eachObject;
va_list argumentList;
if (firstObj) {
[a.items addObject: firstObj];
va_start(argumentList, firstObj);
while (eachObject = va_arg(argumentList, id)) {
[a.items addObject: eachObject];
}
va_end(argumentList);
}
return a;
}
If I initialize my instance like this
FlyNetArrayOfDataSet * arr = [[FlyNetArrayOfDataSet alloc] init];
It's work perfectly and the method isKindOfClass return true :-)

Suppose you have a class named "FlyNetArrayOfDataSet" which inherits from (=is a subclass of) NSArray.
If you instantiate a variable like:
FlyNetArrayOfDataSet *arr = [[FlyNetArrayOfDataSet alloc] init];
As you can see, I'm initializing the array with a method of NSArray. However, my "arr" object will be of kind FlyNetArrayOfDataSet, and NOT NSArray, because I called the FlyNetArrayOfDataSet class (see [FlyNetArrayOfDataSet arrayWithObject....).
NSLog(#"%d", [arr isKindOfClass:[FlyNetArrayOfDataSet class]]);
NSLog(#"%d", [arr isKindOfClass:[NSArray class]]);
Both will return "1", which means "true", because arr is an object of the FlyNetArrayOfDataSet class, which inherits from NSArray.
EDIT
Let's see if I can explain it better:
arr1 = [[FlyNetArrayOfDataSet alloc] init];
arr2 = [[NSArray alloc] init];
Both objects, arr1 and arr2, are created with the same method, which is defined in the class NSArray. However, in the first case the class which is being called is FlyNetArrayOfDataSet and in the second case is NSArray. Thus, arr1 will be an object of class FlyNetArrayOfDataSet, while arr2 will be of class NSArray.
The difference can be seen in this code:
NSLog(#"%d %d",
[arr1 isKindOfClass:[FlyNetArrayOfDataSet class]]
[arr1 isKindOfClass:[NSArray class]]
);
NSLog(#"%d %d",
[arr2 isKindOfClass:[FlyNetArrayOfDataSet class]]
[arr2 isKindOfClass:[NSArray class]]
);
The output of this code is:
1 1 ( = true true)
0 1 ( = false true)

that's because FlyNetArrayOfDataSet is a SoapArray and SoapArray is not a FlyNetArrayOfDataSet.
if datasets were an instance of SoapArray, you will see "Soap" in the following example:
- (SoapRequest*)addFlyData:(id)target
action:(SEL)action
sessionid:(int)sessionid
datasets:(FlyNetArrayOfDataSet*)datasets
{
if ([datasets isKindOfClass:[FlyNetArrayOfDataSet class]]) {
NSLog(#"Fly");
}
else if ([datasets isKindOfClass:[SoapArray class]]) {
NSLog(#"Soap");
}
else {
NSLog(#"???");
}
}
it's possible that an instance of SoapArray is also FlyNetArrayOfDataSet. the other possibilities are:
a SoapArray
subclass other than FlyNetArrayOfDataSet.

Related

Correct way to find out all the superclasses of a Class in Objective-C?

I'm a novice, and I seem to be getting multiple errors on this.
All I want is a for or while loop to print out all the superclasses of a certain Class.
Here is the pseudocode of what I want:
IDontKnowWhatClass *superClassName;
while (superClassName != nil)
{
superClassName = [[superClassName class] superclass];
NSLog(print the name);
}
Try this
Class superClassName = [self class];
while (superClassName != nil)
{
superClassName = [superClassName superclass];
NSLog(#"%#",NSStringFromClass(superClassName));
}
If you know a class itself like NSString then,
Class superClassName = [NSString class];
You can store the class name to a string like
NSString *className = NSStringFromClass(superClassName);
And if you want to create an object of the class from which class name is stored in a string like
id object = [[NSClassFromString(className) alloc] init];
Use NSObject method names as :
objc_property_t class_getProperty(Class cls, const char *name)
//Returns a property with a given name of a given class.
Keep on finding till you get the NSObject as it is supermost class
Use this method to check for equality :
- (BOOL)isEqual:(id)object;
or
[object isKindOfClass:[SomeClass class]]
Documentation here
This will work
You can call superclass method on your current class until it gets equal to Nil (that will happen for root class, i.e. NSObject).
Class c = [IDontKnowWhatClass class];
while (c)
{
NSLog(#"%#", NSStringFromClass(c));
c = [c superclass];
}
NSString* classString = #"IDontKnowWhatClass";
Class class = NSClassFromString(classString);
while (class != nil){
NSLog(#"%#", [class description]);
class = [class superclass];
}

Create a NSSet from NSArray based on property

How does one create a NSSet of objects from an array based on a property.
e.g. Array of objects, each with a strong reference to a type property, and multiple occurrences of each type exist in the array. How can this be turned into an NSSet holding a single object of each type.
NSSet *distinctSet = [NSSet setWithArray:[array valueForKeyPath:#"#distinctUnionOfObjects.property"]];
A dictionary essentially has this functionality already. Its keys are a set, so you can create the dictionary to hold the objects, keyed by whatever attribute you're interested in:
[NSDictionary dictionaryWithObjects:arrayOfObjects
forKeys:[arrayOfObjects valueForKey:theAttribute]];
If you ask the dictionary for allValues now, you have only one object for each attribute. I should mention that with this procedure, the later objects will be kept in favor of earlier. If the order of your original array is significant, reverse it before creating the dictionary.
You can't actually put those objects into an NSSet, because the NSSet will use the objects' isEqual: and hash methods to determine whether they should be members, rather than the key attribute (of course, you can override these methods if this is your own class, but that would likely interfere with their behavior in other collections).
If you really really feel that you need a set, you will have to write your own class. You can subclass NSSet, but conventional wisdom is that composition of Cocoa collections is far easier than subclassing. Essentially, you write a class which covers any set methods you're interested in. Here's a (quite incomplete and totally untested) sketch:
#interface KeyedMutableSet : NSObject
/* This selector is performed on any object which is added to the set.
* If the result already exists, then the object is not added.
*/
#property (assign, nonatomic) SEL keySEL;
- (id)initWithKeySEL:(SEL)keySEL;
- (id)initWithArray:(NSArray *)initArray usingKeySEL:(SEL)keySEL;
- (void)addObject:(id)obj;
- (NSArray *)allObjects;
- (NSArray *)allKeys;
- (BOOL)containsObject:(id)obj;
- (NSUInteger)count;
-(void)enumerateObjectsUsingBlock:(void (^)(id, BOOL *))block;
// And so on...
#end
#import "KeyedMutableSet.h"
#implementation KeyedMutableSet
{
NSMutableArray * _objects;
NSMutableSet * _keys;
}
- (id)initWithKeySEL:(SEL)keySEL
{
return [self initWithArray:nil usingKeySEL:keySEL];
}
- (id)initWithArray:(NSArray *)initArray usingKeySEL:(SEL)keySEL
{
self = [super init];
if( !self ) return nil;
_keySEL = keySEL;
_objects = [NSMutableArray array];
_keys = [NSMutableSet set];
for( id obj in initArray ){
[self addObject:obj];
}
return self;
}
- (void)addObject:(id)obj
{
id objKey = [obj performSelector:[self keySEL]];
if( ![keys containsObject:objKey] ){
[_keys addObject:objKey];
[_objects addObject:obj];
}
}
- (NSArray *)allObjects
{
return _objects;
}
- (NSArray *)allKeys
{
return [_keys allObjects];
}
- (BOOL)containsObject:(id)obj
{
return [_keys containsObject:[obj performSelector:[self keySEL]]];
}
- (NSUInteger)count
{
return [_objects count];
}
- (NSString *)description
{
return [_objects description];
}
-(void)enumerateObjectsUsingBlock:(void (^)(id, BOOL *))block
{
for( id obj in _objects ){
BOOL stop = NO;
block(obj, &stop);
if( stop ) break;
}
}
#end
NSMutableSet* classes = [[NSMutableSet alloc] init];
NSMutableSet* actualSet = [[NSMutableSet alloc] init];
for(id object in array) {
if([classes containsObject:[object class]] == NO) {
[classes addObject:[object class]];
[actualSet addObject:object];
}
}
You would use:
NSSet* mySetWithUniqueItems= [NSSet setWithArray: yourArray];
This should work regardless of the type of objects in your array and would populate the NSSet with only one occurence of any duplicate objects in your array.
I hope this helps.
Update:
Next best thing: is use concatenation of class name and object property first then use the above method.
self.concatenatedArray=[NSMutableArray arrayWithCapacity:4];
for (TheClass* object in self.myArray)
[self.concatenatedArray addObject:[NSString stringWithFormat:#"%#-%#",[object class], object.theProperty]];
self.mySet=[NSSet setWithArray:self.concatenatedArray];
I am not sure what you will use the NSSet output for but you can probably modify the concatenation elements to have the information you need in the NSSet output.
I have created a simple library, called Linq to ObjectiveC, which is a collection of methods that makes this kind of problem much easier to solve. In your case you need the Linq-to-ObjectiveC distinct method:
NSSet* dictionary = [NSSet setWithArray:[sourceArray distinct:^id(id item) {
return [item type] ;
}]];
This returns a set where each item has a distinct type property.

How to test properties of class using TDD?

Why when I use method respondsToSelector: or instancesRespondToSelector: at line 43 I cannot bypass STAssertTrue?
//My test case code
- (void)testApiClass {
//Check object
NSString* classKey = #"Api";
id obj = NSClassFromString(classKey);
STAssertNotNil(obj, [NSString stringWithFormat:#"Model '%#' not found.", classKey]);
//Check properties
NSArray* properties =
#[
#"performSyncRequestWithUri::",
#"performAsyncRequestWithUri:::",
];
for (NSString* property in properties) {
SEL propertySel = NSSelectorFromString(property);
BOOL isRespondsToSel = [obj respondsToSelector:propertySel];
STAssertTrue(isRespondsToSel, [NSString stringWithFormat:#"Property '%#' not found on object of class name '%#'", property, [obj class]]);
}
}
#interface Api : NSObject
- (NSDictionary*)performSyncRequestWithUri:(NSString *)requestUri params:(NSDictionary *)params;
- (void)performAsyncRequestWithUri:(NSString *)requestUri params:(NSDictionary *)params completionHandler:(void (^)(NSDictionary *, NSError *))completionBlock;
#end
The string constants in your properties array don't match the selectors in your Api interface.
Also, neither of those selectors refers to a property. A property has two selectors: a getter, like stringValue, which has no colons, and a setter, like setStringValue:, which has one colon and (usually) starts with set.
Instead of embedding your selectors in strings, make an array of selectors:
SEL selectors[] = {
#selector(performSyncRequestWithUri:params:),
#selector(performAsyncRequestWithUri:params:completionHandler:),
NULL
};
for (size_t i = 0; selectors[i]; ++i) {
SEL selector = selectors[i];
BOOL respondsToSelector = [obj respondsToSelector:selector];
STAssertTrue(respondsToSelector, [NSString stringWithFormat:
#"Object %# doesn't respond to selector %s",
obj, sel_getName(selector)]);
}
The advantages here are that Xcode will autocomplete the selectors for you, and you can command-click the selectors to jump to their definitions.
The methods are called performAsyncRequestWithUri:params:completionHandler: and performSyncRequestWithUri:params:

Method to instantiate objects

Would like to create a method that instantiate objects. 
- (NSArray *) make3Of : (Class) type
{
...
type * temp = [[type alloc] ...
...
}
But I get a warning from Xcode ...
Actual warning:
"Class method +alloc not found (return type defaults to 'id')"
Is there a better/correct way to do this?
Actual code:
- (NSArray *) getBoxesOfType: (Class <ConcreteBox>) type StartingFrom: (uint64_t) offset
{
NSMutableArray *valueArray = [[NSMutableArray alloc]initWithObjects: nil];
for (uint64_t i = offset; i< boxStartFileOffset + self.size; i += [self read_U32_AtBoxOffset:i])
{
if ([[self read_String_OfLen:4 AtBoxOffset:offset + 4] isEqual:[type typecode]]) {
[[type alloc]initWithFile:file withStartOffset:i]; //warning here;
//yes I plan to assign it to a variable
//(originally of "type" but that won't work as AliSoftware pointed out, will be using "id" instead.
...
}
}
}
Same as example, I'm trying to instantiate a couple of objects.
Code for protocol:
#import <Foundation/Foundation.h>
#protocol ConcreteBox
+ (NSString *) typecode;
- (id) initWithFile: (NSFileHandle *) aFile withStartOffset: (uint64_t) theOffset;
#end
You can't use a variable (in your case type)... as a type for another variable!
In your code, both type and temp are variables, that's a syntax error.
As you don't know the type of the variable as compile time, use the dynamic type id instead. This type is specifically designed to handle cases when the type is not defined at compile time.
So your code will look like this:
-(NSArray*)make3Of:(Class)type {
id obj1 = [[[type alloc] init] autorelease];
id obj2 = [[[type alloc] init] autorelease];
id obj3 = [[[type alloc] init] autorelease];
return [NSArray arrayWithObjects:obj1, obj2, obj3, nil];
}

Objective C +(id) methods

I am quite new to Objective-C in some ways, so I'd like to ask how should I make methods that return the objects themselves. Let me show an example:
In NSArray you can do [NSArray arrayWithObjects:bla,bla,nil];
How I make that kind of method to my own class?
There are two main things going on with that method:
It's a Class method (ie, a + method)
It uses a variable argument list
To make it, you'd probably do something like this:
+ (id)fooWithStuff:(id)stuff, ... NS_REQUIRES_NIL_TERMINATION {
// the "+" means it's a class method
// the "NS_REQUIRES_NIL_TERMINATION" is so that the compiler knows you have to use it like this:
// foo = [ThisClass fooWithStuff:thing1, thing2, thing3, nil];
// (IOW, there must be a "nil" at the end of the list)
va_list args; // declare a "variable list"
va_start(args, stuff); // the list starts after the "stuff" argument
Foo *foo = [[Foo alloc] init]; // create a Foo object
id nextStuff = stuff; // this is the next stuff
while(nextStuff != nil) { // while there is more stuff...
[foo doSomethingWithStuff:nextStuff]; // do something with the stuff
nextStuff = va_arg(args, id); // get the next stuff in the list
// the "id" means that you're asking for something the size of a pointer
}
va_end(args); // close the argument list
return [foo autorelease]; // return the Foo object
}