NSValueTransformerMattt 🚩🌱

在 Foundation 框架的所有类中,NSValueTransformer 也许是从 OS X 平台迁移到 iOS 平台表现最差的那个。

为什么?嗯,有两个原因:

第一个也是最明显的一个原因是 NSValueTransformer 主要被用于 AppKit 框架的 Cocoa binding 中。它可以自动地把一个属性的值转换为另一个属性的值,而不需要中间的粘合代码,比如判断一个布尔值,或者检查一个对象是否为 nil。而 iOS 却没有 Cocoa binding 特性。

第二个原因和 iOS 没有多大关系,而是和 Objective-C 运行时有关。自从有了 block,在对象之间传递行为变得简单多了——比起用 NSValueTransformerNSInvocation 简单太多了。所以,即使 iOS 明天就可以使用 Cocoa binding,那也无法确定 NSValueTransformer 现在还是否会发挥那样至关重要的作用。

但是,你猜怎么着?NSValueTransformer 已经成熟很多了。经过一些雕琢和使用场景的改变,也许它能在你的应用中发挥重大作用。


NSValueTransformer 是一个抽象类,它用于把一个值转换为另一个值。它指定了可以处理哪类输入,并且合适时甚至支持反向的转换。

下面是一个常见的实现代码:

@interface ClassNameTransformer: NSValueTransformer {}
@end

#pragma mark -

@implementation ClassNameTransformer
+ (Class)transformedValueClass {
  return [NSString class];
}

+ (BOOL)allowsReverseTransformation {
    return NO;
}

- (id)transformedValue:(id)value {
    return (value == nil) ? nil : NSStringFromClass([value class]);
}
@end

我们通常不会直接初始化 NSValueTransformer。而是,与 NSPersistentStoreNSURLProtocol 类似,需要注册相应的实现类,由管理者角色的对象负责初始化它们——这里有点不同的是,你需要把_对象_注册为一个带有指定名字的单例。

NSString * const ClassNameTransformerName = @"ClassNameTransformer";

// Set the value transformer
[NSValueTransformer setValueTransformer:[[ClassNameTransformer alloc] init] forName:ClassNameTransformerName];

// Get the value transformer
NSValueTransformer *valueTransformer = [NSValueTransformer valueTransformerForName:ClassNameTransformerName];

通常,我们会在 NSValueTransformer 实现类的 +initialize 方法中注册单例对象,这样在需要用它的时候就不用再做别的什么事情了。

到这里你可能发现了 NSValueTransformer 的一个大毛病:它太难用了!创建一个类,实现一大把的方法,声明一个常量,_并且_还要在 +initialize 方法中注册它?我看还是别用算了。

在这个使用 block 的年代,我们想要——不对,需要——一种一行(坨)代码就能搞定的实现方式。

一点儿元编程就可以轻松搞定这件事情。注意啦:

NSString * const TKCapitalizedStringTransformerName = @"TKCapitalizedStringTransformerName";

[NSValueTransformer registerValueTransformerWithName:TKCapitalizedStringTransformerName
           transformedValueClass:[NSString class]
returningTransformedValueWithBlock:^id(id value) {
  return [value capitalizedString];
}];

我并不是为了迎合读者你,但是在写本篇文章时,我特别想看看怎么能够提升使用 NSValueTransformer 的体验。最终的结果就是 TransformerKit

整个库的基础是一个使用了一些 Objective-C 运行时技巧的 NSValueTransformer category。同时包含了一些方便的例子,比如字符串大小写转换(例如,CamelCasellamaCasesnake_casetrain-case)。

现在有了这身漂亮的行头,我们要开始好好想想它可能会在哪里发挥用处:

以及还有很多其他的用处,就不再一一列举了。


NSValueTransformer 并没有 AppKit 框架的印记,它保留了 Foundation 框架与计算基本概念的纯洁联系:接受输入,返回输出。

尽管它还不是很成熟,但一点新鲜的改造可以让 NSValueTransformer 回到 NS-嬉皮士界(NSHipsterdom)的最高宗旨:那些我们尚不知道有用的解决方案其实一直在那等待着我们发现。


除非另有声明,本文采用知识共享「署名-非商业性使用 3.0 中国大陆」许可协议授权。