7

Is it possible to pass a unknown type of parameter to a objective-C method?

In C# you can write <T> to achieve this, but I know that objective-C doesn't have generics so are there some other way to make this possible in objective-C?

I need this because I want to create a method that changes the text color of different objects like placeholder text of a UITextField and UIButton. So my plan was to create one method called textWhite and then inside this method to check for the kind of object and then run matching code to make the text color white.

san
  • 3,350
  • 1
  • 28
  • 40
user2408952
  • 2,011
  • 5
  • 24
  • 26

4 Answers4

7

Yes, it is possible to pass unknown type of parameter. See below example.

-(void) fooMethod:(id)unknownTypeParameter {
  if( [unknownTypeParameter isKindOfClass:[Animal Class]]) {
      Animal *referanceObj = (Animal *) unknownTypeParameter;
      referanceObj.noOfLegs = 4;
  }
}

Refer link for use id object as parameter Using (id) in Objective-C

Community
  • 1
  • 1
Akshay Nalawade
  • 1,447
  • 2
  • 14
  • 29
  • how do i get a property on unknownTypeParameter then? etc unknownTypeParameter.attributedPlaceholder – user2408952 Jan 20 '14 at 09:14
  • @user2408952 use can either cast it or else use the object for key **[unknownTypeParameter valueForKey:@"attributedPlaceholder"];** – CoolMonster Jan 20 '14 at 09:21
  • when you say i can either use one or another does that mean theres no right and wrong? – user2408952 Jan 20 '14 at 09:25
  • 1
    Its generally preferable to replace 'instanceof' with polymorphism. – Jasper Blues Jan 20 '14 at 09:27
  • I will prefer type casting over another method. It gives more control over an object. – Akshay Nalawade Jan 20 '14 at 09:27
  • Akshay Nalawade is it better to create a new object, then casting like @user2408952 described. Reason why i ask is that creating a new object would take up more memory yes? – user2408952 Jan 20 '14 at 09:32
  • Yes, creation of new object will take more memory but in this case you don't have to create new object, simply type cast and assign object to newly created reference object. – Akshay Nalawade Jan 20 '14 at 09:37
  • I'm new to pointers so i don't 100% understand how they work, but what i get from your answer is that a reference object wont take nearly as much memory as a new object would? Btw i accepted your answer thanks a lot for the help. i hope you would answer this last question. – user2408952 Jan 20 '14 at 09:46
  • 2
    @user2408952 Unlike C++, all objects are pointers in Objective-C (as they are in C#). An object is allocated once, and there can be any number of variables pointing to it. . there's no way for a protocol to *not* be a pointer so the star is left off - hooray for quirkiness! . . (technically an Objc object could not be a pointer, but in practice they always are).. . you're right: A pointer just sits on the stack pointing at the heap-allocated object's memory. Only taking up one RAM address. – Jasper Blues Jan 20 '14 at 11:06
  • ok BIG THANKS^^ that really helped a lot, kudos for all the help guys. – user2408952 Jan 20 '14 at 12:45
2

You can use the anonymous type id for that. Don't forget to include a check if the object responds to the selectors you're going to use.

Alexander
  • 8,117
  • 1
  • 35
  • 46
2

It sounds as though you need duck-typing, which is supported in Objective-C in the following ways:

  1. Use a protocol - this works the same as an interface in C#. The base protocl is id<NSObject> (or less correctly, just 'id')
  2. Use an abstract base class (A special case of this in Objective-C is the class cluster).
  3. Use categories to mix-in interface conformance on a class.
  4. Two objects deriving from completely different base classes / protocols that respond to the same method signature can happily be cast to each other in order to invoke that method.
  5. Use performSelector, NSInvocation or objc_msgSend to invoke a method, if you know the object will happily respond to it.

Common Approach:

For very loosely defined protocols, approach number four is common: We declare a method as follows:

- (void)saveToInventory:(id)inventoryItem
{
    if ([inventoryItem respondsToSelector:@selector(sendToWarehouse]) 
    { 
        id<Warehouseable> warehouseable = (id<Warehouseable>) inventoryItem; 
        [warehouseable sendToWarehouse];
    }
}

. . . while this approach works, it doesn't have a great deal of rigor. Its better to define a stricter protocol.

Rigorous Approach: Replace 'isKindOfClass/respondsToSelector' with polymorphism:

In general its good practice to replace 'isKindOfClass' with polymorphism. Rather than check for a given type, define an interface contract and let a given instance handle this as required. This leads to cleaner, more maintainable code.

In objective-C, the dynamic method dispatch system, makes mix-ins very easy where in C# or Java they require sophisticated libraries. Therefore, even classes that come from a 3rd party, and for which you don't have the source can be extended to all conform to a given protocol. Just define a category method on each class to perform the work. Be sure to use a method name that won't lead to a namespace collision. Example:

@protocol Themeable

-(void)setTheme:(Theme*)theme

@end

UITextField(ThemeAdditions)<MyAppThemeable>

- (void)setTheme:(Theme*)theme 
{
    //Now your text field can respond to this interface
}

Generics in Objective-C:

You mentioned generic-types in your example above. Its not clear how this fits in. In any case its not explicitly supported in Objective-C, although it can be retro-fitted with some caveats. Here's a library that does this: https://github.com/tomersh/Objective-C-Generics

Note that even without generics, its generally not necessary to explicitly cast in Objective-C: NSArray, NSSet, etc all use id as the type being stored. . . this of course doesn't give you compile-time checking. We generally live without this, although if its badly needed something like the library above could help.

Jasper Blues
  • 28,258
  • 22
  • 102
  • 185
2

One thing to add to the current answers.

You could use a protocol for this kind of thing. That way you can inform the method that the object being passed in conforms to a certain protocol and the method will know about the properties and methods declared in that protocol.

For instance...

MyProtocol.h

@protocol MyProtocol <NSObject>

    @property (nonatomic, strong) NSString *someString;
    @property (nonatomic, strong) Person *person; //some custom class

    - (void)doSomething;

@end

Then in your method you can declare it like the following...

- (void)foo:(id <MyProtocol>)protocolObject
{
    NSLog(@"%@", protocolObject.someString);

    Person *person = protocolObject.person;

    [protocolObject doSomething];
}

This way you can use any object type without having to do if...else for all the different possibilities. You just need to include the protocol declaration and you know it works.

Just to add a nice summary to this...

In order to interact with an object you just need to know HOW it works. You don't need to know WHAT it is. By doing this with a protocol you are allowing yourself to interact in this way.

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
  • 1
    Agree 100% . . and in OP's case, I would mix in that protocol to the classes requiring it. . . The book 'Refactoring to Patterns' defines this as 'replace instanceof with polymorphism' . . I guess in objective-c we'd call it 'replace respondsToSelector with polymorphism' – Jasper Blues Jan 20 '14 at 09:54