Objective-C中为什么不支持泛型方法

今天和网易的一位朋友交流,我和他说,我下面的app会用Swift+函数式编程方式去写,然后他说

Swift大公司都用的较少, 因为新东西,都怕不稳定, 尽量把Objective-C学的精深一些

是的,最近一段时间我看了一些Python的书,使用web.py和django写了点模仿豆瓣和拉钩的demo,我也在学习Swift,使用ReactiveCocoa。最近的时间都花在了这个上面,这是否是一种舍本求末的做法,我不知道,但是Desperado同志确实提醒了我,Objective-C是我现在的饭碗。(另外,我再瞻仰一下Desperado在北京的工资...)

言归正转,我们一起来探究一下Objective-C中为什么不支持泛型方法。

每当探讨此类语言级别对某一个特性的支持的时候,其实很多时候是在探讨这个语言的设计者对这门语言的设计哲学。比如说,很多语言不支持多继承,可能并不是实现不了,也不是不好实现(松本行弘的Ruby就使用了一个很好的多继承实现方式),可能是作者认为马就应该是马,驴就应该是驴,不存在一个又是马、又是驴的物种,顶多是一个物种具有另一个物种的某些特性和行为。这只是我的猜测,别当真。

这里,我尝试从Objective-C的Method方法的具体实现上去解释为什么Objective-C不支持泛型方法。

首先,我们去看一下Objective-C中的方法”Method”是怎样的一个数据结构。 我们先下载一下苹果开源的Objective-C runtime的源码,在下面的地址上:

http://opensource.apple.com/tarballs/objc4/objc4-646.tar.gz

以这个版本为例,我们在

objc-private.h

的第234-244行找到了如下的内容:

#if __OBJC2__
typedef struct method_t *Method;
typedef struct ivar_t *Ivar;
typedef struct category_t *Category;
typedef struct property_t *objc_property_t;
#else
typedef struct old_method *Method;
typedef struct old_ivar *Ivar;
typedef struct old_category *Category;
typedef struct old_property *objc_property_t;
#endif

也就是说在Objective-C 2.0和Objective-C 2.0以下,整个这个Objective-C中的方法的数据结构是不一样的,那我们直接看Objective-C 2.0之后的Method的数据结构,也就是这行对应的内容:

typedef struct method_t *Method;

我们在

objc-runtime-new.h

的第84-97行找到如下内容:

struct method_t {
    SEL name;
    const char *types;
    IMP imp;

    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};

首先,我们来理解一下这个结构体:

method_t中定义了三个成员变量,和一个成员函数,成员变量分别是

SEL name
const char *types
IMP imp

SEL

我们有时会用到这样的方法,比如说:

[self performSelector:<#(SEL)#> withObject:<#(id)#>]

这里面的SEL,我们一般会这个写

@selector(ok)

那么我们其实可以这么理解,@selector其实就是一个指向于某个方法的一种Objective-C消息,只是这个消息不仅可以发送到成员方法,也可以发送到类方法,SEL和这个是一个类型。

IMP

IMP说简单点的话,就是一个指向于方法的函数指针,在runtime.h中是说明的

A pointer to the function of a method implementation.

和上面的SEL有一定的相似之处,IMP可以通过这样的方式从SEL上取到

IMP imp = [self methodForSelector:selector];

这个指针是直接指定一个方法的实现,而不是通过Objective-C的消息传递的机制去实现。

好的,我们来尝试解释一下上面的Method的数据结构:

SEL name

这个name表示这个Method的方法名,比如说,这样的一个方法

- (void)setName:(NSString *)name

在这边就会是这样的

@selector(setName:)

const char *types

这个表示的是方法的返回值和参数类型。

IMP imp

这个就是上面说到的函数指针,真正指向于一个方法的实现。

struct SortBySELAddress

这个就比较简单了,这个是依据方法名的地址进行排序的一个函数。

那么,回归我们的主题,为什么Objective-C不支持方法泛型呢,我们看到区别于一个Method的关键在于这个name, 在name的写法上,下面两种其实都是同一种东西:

- (void)setName:(NSString *)name
- (void)setName:(int)age

都会写成:

@selector(setName:)

这个是无法区分的。

但是如果一个是类方法,一个是成员方法是可以的:

- (void)setName:(NSString *)name
+ (void)setName:(NSString *)name

因为类方法会保存到元类对象里面,而成员方法会保存到类对象里面,所以并不冲突。

我是在从源码的角度说明为什么Objective-C不支持方法泛型,只是在Objective-C的实现上作解释,这应该不是一种比较好的办法,我想,真正不支持方法泛型的原因应该还是在这门语言的设计哲学上。

Article by 付军