Stavash
Stavash

Reputation: 14304

Determining the receiver of an assignment at runtime in Objective-C

Let's say I have a class with 2 properties:

@interface MyClass ()

@property (strong, nonatomic) NSString *strA;
@property (strong, nonatomic) NSString *strB;

@end

And somewhere in that class I have a method that is supposed to assign a value to one of these properties. The issue is - the property is to be determined during runtime. How would I go about doing this? I want to do this without code replication (could be a very complex method). For instance, this is bad:

- (void) mutateProperty:(BOOL)assignToA
{
   if (assignToA)
   {
       self.strA = @"newStr";
   }
   else
   {
       self.strB = @"newStr";
   }
}

I would like to be able to accomplish something similar to this:

- (void) mutateProperty:(BOOL)assignToA
{
   NSString *propertyToUse = nil;
   if (assignToA)
   {
       propertyToUse = self.strA;
   }
   else
   {
       propertyToUse = self.strB;
   }

   propertyToUse = @"newStr" //But this will only change the propertyToUse variable's value and not affect the scope outside the method.
}

Upvotes: 0

Views: 91

Answers (1)

Nikolai Ruhe
Nikolai Ruhe

Reputation: 81868

The posted code is not an assignment, its a message sent to self:

self.strA = @"newStr";

really is

[self setStrA:@"newStr"];

(The method name might differ in case you declared a custom setter for the property.)

So your problem is about dispatching methods. There are many ways to do this which all differ in details. Yet for your case, where there is one point in code where you only select among two choices I'd go with the plain conditional you already posted. It's most readable and code duplication is minimal.

If your case is more complicated than in your posted code you might want to use some different means of dispatch.

The most basic way to dispatch in Objective-C is to manually calculate the selector:

SEL selector = NULL;
switch (status) {
    case 1:
        selector = @selector(setStrA:);
        break;
    case 2:
        selector = @selector(setStrB:);
        break;
    case 3:
        selector = @selector(setStrC:);
        break;
}
[self performSelector:selector withObject:@"newStr"];

This does not go along well together with ARC as ARC cannot use the selector's name to determine argument and return type ownership. You have to silence the compiler warning and that's ugly.

As pointed out by nielsbot a built in way to set arbitrary properties is KVC: You can calculate a "key" that is used by KVC to find the matching method:

NSString *key = nil;
switch (status) {
    case 1:
        key = @"strA";
        break;
    case 2:
        key = @"strB";
        break;
    case 3:
        key = @"strC";
        break;
}
[self setValue:@"newStr" forKey:key];

That's a bit more overhead but it also works for POD argument types.

Upvotes: 2

Related Questions