Polymorphism in Objective C

Member
Posts: 110
Joined: 2009.07
Post: #1
I'm trying to write one function that'll create a dictionary of a given class based on a file I'm loading. I'm doing it because at the moment, a near identical function exists in about 5 different unrelated classes and it seems like a bad way to write the code.

Instead, I'm trying to write a polymorphic function (if that's the right term) that looks something like this:

Code:
- (void) setupDataDictionary: (NSMutableDictionary *) dict WithFilename: (NSString *) file OfClass: (Class) class
{
    CParser *parser = [[CParser alloc] initWithFilename: file];
    
    if(parser != nil)
    {            
        NSString *line;
        while((line = [parser getCurrentLine]) != nil)     
        {        
// Not sure how I write the line below!
            class *data = [[class alloc] initWithDataParser: parser];
            if(data != nil)
            {
                [dict setValue: data forKey: data.name];
            }
            [parser nextLine];
            [data release];
        }
        
        [parser release];
    }
}

Obviously, there's one line in there (at least) that makes me look like a moron. However, I've never passed a class-type to a function before, and it's all a bit strange to me.

Any suggestions?

http://www.fluttermind.com
Fluttermind - Games for Everyone
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #2
My Obj-C is not as strong as my Ruby, but I think that class objects can be used in the same way you would normally use an actual class name. They have the same methods, but the compiler can't check that the methods exist. Basically the class type is to classes what id is to objects.

So I think you would do this:
Code:
id data = [[class alloc] initWithDataParser: parser];

The runtime does all method calls as dynamic anyway, so you are just taking type information away from the compiler is all.

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
⌘-R in Chief
Posts: 1,256
Joined: 2002.05
Post: #3
Skorche is right.
Quote this message in a reply
Member
Posts: 86
Joined: 2008.04
Post: #4
Have you looked at NSCoder?
It is great for serialization.

You can also directly access type encodings:
http://developer.apple.com/documentation...dings.html
Quote this message in a reply
Member
Posts: 110
Joined: 2009.07
Post: #5
FreakSoftware Wrote:Skorche is right.

Thanks folks - that seems to fix it.

http://www.fluttermind.com
Fluttermind - Games for Everyone
Quote this message in a reply
Member
Posts: 110
Joined: 2009.07
Post: #6
For those who are interested (and to ensure I put something back into the community rather than just take all the time), here's my code. I love it. It's elegant, and does exactly what you'd expect it to:

Code:
// The main big parser thing
- (NSMutableDictionary *) dataDictionaryFromFilename: (NSString *) file OfClass: (Class) class
{    
    NSMutableDictionary *dict = [[[NSMutableDictionary alloc] init] autorelease];

    [self setupWithFilename: file];
    NSString *line;
    while((line = [self getCurrentLine]) != nil)     
    {        
        id data;
        data = [class alloc];
        SEL init_function = NSSelectorFromString(@"initWithDataParser:");
        if([data respondsToSelector: init_function])
            [data performSelector: init_function withObject: self];
        
        if(data != nil)
        {
            [dict setValue: data forKey: [data name]];
        }
        [self nextLine];
    }
    
    return dict;
}

http://www.fluttermind.com
Fluttermind - Games for Everyone
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #7
Not sure what you were going for here exactly, but if the object doesn't respond to the selector, you are calling "name" on an uninitialized object.

Code:
        data = [class alloc];
        SEL init_function = NSSelectorFromString(@"initWithDataParser:");
        if([data respondsToSelector: init_function])
            [data performSelector: init_function withObject: self];

        // data might be non-nil, but uninitialized here
        if(data != nil)
        {
            [dict setValue: data forKey: [data name]];
        }

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
Member
Posts: 110
Joined: 2009.07
Post: #8
Well spotted. Thanks! You've saved me future headaches. Guess who doesn't do unit testing...

...or read his own code, apparently.

http://www.fluttermind.com
Fluttermind - Games for Everyone
Quote this message in a reply
⌘-R in Chief
Posts: 1,256
Joined: 2002.05
Post: #9
Madrayken Wrote:
Code:
// The main big parser thing
- (NSMutableDictionary *) dataDictionaryFromFilename: (NSString *) file OfClass: (Class) class

Don't capitalize the first letter of each part of the selector. In other words OfClass should be ofClass. Follow the convention.
Quote this message in a reply
Member
Posts: 110
Joined: 2009.07
Post: #10
Code now looks like this:

Code:
- (NSMutableDictionary *) dataDictionaryFromFilename: (NSString *) file ofClass: (Class) class
{    
    NSMutableDictionary *dict = [[[NSMutableDictionary alloc] init] autorelease];

    [self setupWithFilename: file];
    NSString *line;
    while((line = [self getCurrentLine]) != nil)     
    {        
        id data;
        
        data = [class alloc];

        SEL init_function = NSSelectorFromString(@"initWithDataParser:");
        if([data respondsToSelector: init_function])
        {
            data = [data performSelector: init_function withObject: self];
            if(data != nil)
            {
                [dict setValue: data forKey: [data name]];
            }
        }
            
        [self nextLine];
    }
    
    return dict;
}

What I really wanted was for that central block to be:

Code:
    id data;
        
SEL init_function = NSSelectorFromString(@"initWithDataParser:");
if([class respondsToSelector: init_function])
{
    data = [[class alloc] performSelector: init_function withObject: self];
    if(data != nil)
    {
        [dict setValue: data forKey: [data name]];
    }
}

However, I don't see a way to perform the selector check on a class. The compiler is happy, but the respondsToSelector: check fails every time.

http://www.fluttermind.com
Fluttermind - Games for Everyone
Quote this message in a reply
Member
Posts: 110
Joined: 2009.07
Post: #11
Furthermore, regarding lower-casing parts of method names, if I presume that means I should have functions of the form:

setVertexCoordX: y: z:

rather than:

setVertexCoordX: Y: Z:

This is where I stumbled last time - the former looks counter-intuitive, and indeed, counter camel-script. I presume that's just me, though?

http://www.fluttermind.com
Fluttermind - Games for Everyone
Quote this message in a reply
Moderator
Posts: 3,573
Joined: 2003.06
Post: #12
Madrayken Wrote:Furthermore, regarding lower-casing parts of method names, if I presume that means I should have functions of the form:

setVertexCoordX: y: z:

rather than:

setVertexCoordX: Y: Z:

This is where I stumbled last time - the former looks counter-intuitive, and indeed, counter camel-script. I presume that's just me, though?

If it makes sense to break with convention and capitalize, then do it. In your excellent example, I'd take the liberty of doing setVertexCoordX: Y: Z:

Otherwise, yes, as FreakSoftware mentioned, it's the convention to start them off with lower case.
Quote this message in a reply
Member
Posts: 110
Joined: 2009.07
Post: #13
AnotherJake Wrote:If it makes sense to break with convention and capitalize, then do it. In your excellent example, I'd take the liberty of doing setVertexCoordX: Y: Z:

Otherwise, yes, as FreakSoftware mentioned, it's the convention to start them off with lower case.

Thanks, folks. I'm new to the whole objective-C thing, and my hacky nature occasionally reveals itself.

http://www.fluttermind.com
Fluttermind - Games for Everyone
Quote this message in a reply
Post Reply