Objective-C中的模拟方法重载

时间:2013-12-02 17:39:12

标签: objective-c overloading

我正在Objective-C中编写一个数学库(为了好玩),现在我需要一些方法来模拟C ++样式的函数重载。

我的意图:

NSNumber *x = @(25);
CMVector *v = [CMVector vectorWithElements:@12, @14, @18, nil];
CMMatrix *m = [CMMatrix matrixWithRows:@[@12, @13, @0 ],
                                       @[@24, @26, @30],
                                       @[@0,  @0,  @1 ], nil];
CMMatrix *i = [CMMatrix identityMatrixWithRank:3];

NSLog(@"%@ %@ %@ %@", [x multiply:v], [x multiply:m], [m multiply: i], [v multiply:x]);

有没有办法实现这个?

这是我提出的一种可能的方法,使用类来表示重载函数并将参数名称编码为类方法名称,如:

@interface CMMultiply : NSProxy

+ (NSNumber *)NSNumber:(NSNumber *)left NSNumber:(NSNumber *)right;
+ (CMVector *)NSNumber:(NSNumber *)left CMVector:(CMVector *)right;
+ (CMVector *)CMVector:(CMVector *)left NSNumber:(NSNumber *)right;
+ (CMVector *)CMVector:(CMVector *)left CMVector:(CMVector *)right;
+ (CMMatrix *)NSNumber:(NSNumber *)left CMMatrix:(CMMatrix *)right;
+ (CMMatrix *)CMMatrix:(CMMatrix *)left NSNumber:(NSNumber *)right;
+ (CMMatrix *)CMMatrix:(CMMatrix *)left CMMatrix:(CMMatrix *)right;

@end

并通过创建编码的方法名称来实现重载方法并调用它。

有更好的想法吗?

修改

这是我的重载逻辑:

@implementation NSObject (CMOverloading)

- (NSString *)overloadingClassName
{
    return NSStringFromClass([self class]); // Override this to handle class clusters
}

- (id)callOverloadedMethod:(Class)method withObject:(id)other
{
    NSString *methodName = [NSString stringWithFormat:@"%@:%@:", [self overloadingClassName], [other overloadingClassName]];
    SEL selector = NSSelectorFromString(methodName);
    return objc_msgSend(method, selector, self, other);
}

@end

编辑2

“重载库”的基类是NSProxy,因此库的用户无法实例化它。

编辑3

简化宏的实现:

#define CMOverloadingMethod(_return, _left, _right) \
+ (_return *)_left:(_left *)left _right:(_right *)right

#define CMOverloadedMethod(_type, _object) \
return [self callOverloadedMethod:[_type class] withObject:(_object)]

重载方法仍需要(简单)单一实现,此处直接在NSObject中:

- (id)multiply:(id)right
{
    CMOverloadedMethod(CMMultiply, right);
}

并按NSNumber

实施
@implementation CMMultiply (NSNumber)

CMOverloadingMethod(NSNumber, NSNumber, NSNumber)
{
    return @([left doubleValue] * [right doubleValue]);
}

@end

4 个答案:

答案 0 :(得分:2)

实现此语法

NSLog(@"%@ %@ %@ %@", [x multiply:v], [x multiply:m], [m multiply: i], [v multiply:x]);

你必须在每个数学类(向量,矩阵)和NSNumber上的类别方法中实现乘法。问题是您还希望能够传递任何参数,而Objective-C(具有相同名称但参数类型不同的多个方法)不支持此参数。

相反,您可以将id作为参数并在运行时计算出类型,或者您必须更详细:

NSLog(@"%@ %@ %@ %@", [x multiplyWithVector:v], 
                      [x multiplyWithMatrix:m],
                      [m multiplyWithMatrix:i],
                      [v multiplyWithScalar:x]);

但是这太复杂了,而且实现这样的数学库会很慢。

为什么不使用简单的结构和函数呢?这已经做了很多次,它的工作原理。

答案 1 :(得分:1)

Objective-C不使用像C ++这样的vtable。它指的是使用字符串查找的方法,因此您不能通过参数重载方法。那么,您最好的选择是使用id来引用任何类型的参数。所以你的multiply方法看起来像这样:

+ (id)multiplyLeft:(id)left right:(id)right;

不是最漂亮的解决方案。

你可以使用类别,然后你就会有一个像这样开始的解决方案:

@interface NSObject (Math)

- (instancetype)multiply:(id)right;

@end

@implementation NSObject (Math)

- (instancetype)multiply:(id)right
{
    // Feel free to throw an exception here.
    return nil;
}

@end

@implementation NSNumber (Math)

- (instancetype)multiply:(id)right
{
    if ([right isKindOfClass:[NSNumber class]])
    {
        return [NSNumber numberWithInteger:[self integerValue] * [right integerValue]];
    }
    else
    {
    // Feel free to throw an exception here.
        return nil;
    }
}

@end

同样,这不是一个漂亮的解决方案,但它允许你写下你想要的东西。

祝你好运。

答案 2 :(得分:0)

您也可以使用C ++。 Objective-C ++非常强大。 Clang现在支持C ++ 11.这是C ++做得很好的东西,并且混合两种语言的代码非常干净和简单。

答案 3 :(得分:0)

这是我使用的最终设计,使用反射解析类型。我仍然使用类作为重载函数的容器,基本上使用这些容器类型作为vtable:

#define CMOverloadingMethod(_return, _left, _right) \
+ (_return *)_left:(_left *)left _right:(_right *)right

#define CMOverloadedMethod(_type, _object) \
return [self callOverloadedMethod:[_type class] withObject:(_object)]

- (id)callOverloadedMethod:(Class)method withObject:(id)other
{
    Class thisClass = [self class];
    Class nextClass = [other class];
    Class methodClass = object_getClass(method);

    // Find the appropriate overload:
    Class thisClass2 = Nil;
    do
    {
        thisClass2 = thisClass;

        Class otherClass = nextClass;
        Class otherClass2 = Nil;
        do
        {
            otherClass2 = otherClass;
            SEL selector = NSSelectorFromString(MSSTR(@"%@:%@:", NSStringFromClass(thisClass), NSStringFromClass(otherClass)));
            if (class_respondsToSelector(methodClass, selector))
                return objc_msgSend(method, selector, self, other);
            else
                otherClass = class_getSuperclass(otherClass);
        } while (otherClass != otherClass2 && otherClass && otherClass2);
        thisClass = class_getSuperclass(thisClass);
    } while (thisClass != thisClass2 && thisClass && thisClass2);

    [NSException raise:NSInvalidArgumentException format:@"Cannot resolve overloaded method in class %@ for objects typed %@ and %@.", NSStringFromClass(method), NSStringFromClass([self class]), NSStringFromClass([other class])];
    return nil;
}