EnumeratorKit 是一个集合枚举库,其设计灵感来源于 Ruby 中的 Enumerable
模块和 Enumerator
类。
它允许您以非常紧凑、表达力强的way与对象集合一起工作
#import <EnumeratorKit/EnumeratorKit.h>
NSArray *numbers = @[ @1, @2, @3 ];
// perform the block once for each element in the collection
[numbers each:^(id i){
NSLog(@"%@", i);
}];
// return a new array with each element converted to a string
[numbers map:^(id i){
return [i stringValue];
}];
此外,这些操作可以连锁在一起,形成更高级别的操作
NSDictionary *examples = @{ @"Hello": @"world", @"foo": @"BAR" };
[[[examples sortBy:^(id pair){
// sort entries by their keys, case insensitive
return [pair[0] lowercaseString];
}] map:^(id pair){
// combine each key-value pair into a new string
return [NSString stringWithFormat:@"%@ %@", pair[0], pair[1]];
}] select:^(id pair){
return [pair[1] hasSuffix:@"orld"];
}];
// => @[@"Hello world"];
EnumeratorKit 为核心集合类(以及它们的可变版本)实现了这种功能: NSArray
、NSDictionary
、NSSet
和 NSOrderedSet
。
任何遵守 NSFastEnumeration
的集合都可以方便地使用 ek_enumerate
函数包装
[ek_enumerate(@[@1, @2, @3]) map:^(NSNumber *i) {
return @(i.integerValue * 2);
}];
// => @[@2, @4, @6]
EnumeratorKit 还提供了 EKEnumerator
类,它相比 NSEnumerator
有许多优点,例如
EKEnumerable
API// you may have seen this in Ruby before
EKEnumerator *fib = [EKEnumerator new:^(id<EKYielder> yielder){
int a = 1, b = 1;
while (1) { // infinite loop (!)
[yielder yield:@(a)];
NSUInteger next = a + b;
a = b; b = next;
}
}];
[[fib take:10] asArray]; // => @[ @1, @1, @2, @3, @5, @8, @13, @21, @34, @55 ]
EnumeratorKit 通过 CocoaPods 可用。将其添加到您的 Podfile
中。
pod 'EnumeratorKit', '~> 0.1.1'
(不要忘记运行 pod install
.) 然后导入头文件
#import <EnumeratorKit/EnumeratorKit.h>
现在您可以继续使用它了。
(注意:如果您只想使用 EKFiber
,而不使用 EKEnumerable
的其余部分,您可以使用此子规范: pod 'EnumeratorKit/EKFiber'
.)
EKEnumerable
类定义了一个基于一个操作的方法的 API
- (instancetype)each:(void (^)(id obj))block;
不同的集合类实现了自己的 -each:
版本,以便定义其元素遍历的顺序。例如,在 NSArray
上,-each:
被定义为依次对每个项执行一次。对于 NSDictionary
,-each:
构建一个键值对的两个元素数组 @[key, value]
并将其传递给块,每次传递一个条目。
EnumeratorKit会使用Objective-C运行时将所有在EKEnumerable
中定义的方法“混合”到另一个类中(类似于Ruby模块)。这类似于使用Objective-C类别向现有类添加方法,但是完全在运行时完成。(还有一个附加优点,如果您继承自EKEnumerable
类,可以覆盖EKEnumerable
提供的任何方法。但是,类别方法会破坏多态性。)
通过用-each:
定义API,任何类都可以通过实现该方法和混合EKEnumerable
来获得该功能。
Kiwi测试提供很多有用的示例。(如果您想了解更多,我还建议查看Ruby的Enumerable文档。)以下是支持的操作的快速浏览:
-each
—— 对集合中的每个项目执行块一次-eachWithIndex
—— 与-each
类似,但是在块中将当前索引作为第二个参数传递-asArray
—— 获取任何枚举的数组表示-take
—— 从枚举的开始处获取指定数量的元素-map
—— 对集合中的每个项目应用块,返回转换后的值的新集合-select
—— 创建一个新枚举,包含块返回YES
的所有元素-find
—— 返回第一个块返回YES
的元素,如果没有找到匹配元素,则为nil
-any
—— 检查集合中的元素是否通过块-all
—— 检查集合中的所有元素是否通过块-sort
—— 返回一个排序数组(集合中的项目必须响应compare:
)-sortWith
—— 与-sort
类似,但允许您指定一个NSComparator
-sortBy
—— 使用将块应用于每个元素的结果作为排序接收者的排序键进行排序-reduce
—— 遍历枚举,在每一步中对块进行评估,并累积新值(例如,将数字数组“减少”为一个表示总和的单个数字)您非常容易在您自己的集合类中获得整个EKEnumerable
API的好处
EKEnumerable
协议# import <EnumeratorKit.h>
@interface MyAwesomeCollection : NSObject <EKEnumerable>
...
@end
+load
和includeEKEnumerable
+ (void)load
{
[self includeEKEnumerable];
}
-each:
,和-initWithEnumerable:
@implementation MyAwesomeCollection
.
.
.
- (instancetype)initWithEnumerable:(id<EKEnumerable>)enumerable
{
// traverse the enumerable, adding each item to your collection
}
- (instancetype)each:(void (^)(id))block
{
// hypothetical enumeration code
for (int i; i < self.length; i++) {
// call the block, passing in each element
block(self[i]);
}
// make sure you return self, to enable enumerator chaining
return self;
}
.
.
.
@end
我建议在花费大量时间开发新功能之前先打开一个问题。然而,如果您的更改相对独立,通常更容易以拉取请求的形式进行评估。
实现这个项目的第一个版本让我对Objective-C以及Ruby的Enumerable
和Enumerator
功能有了很多了解。特别感谢