美文网首页
Objective-C集合类使用指南

Objective-C集合类使用指南

作者: 读行笔记 | 来源:发表于2020-11-12 22:16 被阅读0次

    NSArray

    排序sort

    Selector

    通过Selector排序是最常用的方法,而且也是速度最快的。sortedArrayUsingSelector::

    NSArray *array = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    // 字符串通常使用`localizedCaseInsensitiveCompare:`、`localizedCompare:`,或者最普通的`compare:`
    NSArray *sortedArray = [array sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
    
    // 数字排序通常使用`compare:`即可
    NSArray *numbers = @[@9, @5, @11, @3, @1];
    NSArray *sortedNumbers = [numbers sortedArrayUsingSelector:@selector(compare:)];
    

    Block

    使用Block进行排序更加方便,使用地更多。sortedArrayUsingComparator:

    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    // NSComparator是一个预定义的Block:typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
    sortedStrs = [strs sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        return [obj1 caseInsensitiveCompare:obj2];
    }];
    
    // 还可以设置option,用`NSSortConcurrent`提高排序速度
    sortedStrs = [strs sortedArrayWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
            return [obj1 caseInsensitiveCompare:obj2];
        }];
    

    函数指针

    也可以通过更加原始的函数指针来排序。sortedArrayUsingFunction:context:

    // 先定义函数
    NSInteger sort(id obj1, id obj2, void* context) {
        if ([obj1 valueForKey:@"length"]) {
            if ([obj1 length] > [obj2 length]) {
                return NSOrderedDescending;
            }
        } else {
            if ([obj1 intValue] > [obj2 intValue]) {
                return NSOrderedDescending;
            }
        }
        
        return NSOrderedAscending;
    }
    
    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    sortedStrs = [strs sortedArrayUsingFunction:sort context:nil];
    
    // 还可以通过`sortedArrayHint`提高排序速度
    sortedStrs = [strs sortedArrayUsingFunction:sort context:nil hint:[strs sortedArrayHint]];
    

    NSSortDescriptor

    用官方话语来说,NSSortDescriptor就是为了对一系列对象进行排序操作,具体而言就是要指定一个key,也就是对象的property作为基准,进行排序。

    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    // 通过NSString的length属性进行排序
    sortedStrs = [strs sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"length" ascending:YES]]];
    

    用一个比较真实的例子测试一下NSSortDescriptor的用法。下面定义了一个Product类:

    // 📃 WRProduct.h
    @interface WRProduct : NSObject
    
    @property (nonatomic, assign) NSUInteger sales;
    
    @property (nonatomic, copy) NSString *name;
    
    + (instancetype)productWithName:(NSString *)name sales:(NSUInteger)sales;
    
    @end
    
    @interface WRProductLab : NSObject
    
    + (NSArray<WRProduct *>*)testProducts;
    
    + (WRProduct *)randomProductFromTest;
    
    @end
    
    // 📃 WRProduct.m
    @implementation WRProduct
    
    + (instancetype)productWithName:(NSString *)name sales:(NSUInteger)sales{
        return [[self alloc] initWithName:name sales:sales];
    }
    
    - (instancetype)initWithName:(NSString *)name sales:(NSUInteger)sales{
        if ((self = [super init])) {
            self.name = name;
            self.sales = sales;
        }
        return self;
    }
    
    - (NSString *)description{
        return [NSString stringWithFormat:@"product name: %@, sales: %lu", self.name, (unsigned long)self.sales];
    }
    
    @end
    
    @implementation WRProductLab
    
    + (NSArray<WRProduct *> *)testProducts{
        NSMutableArray *m = [@[] mutableCopy];
        
        for (int i=0; i<10; i++) {
            NSUInteger sales = arc4random()%10000;
            [m addObject:[WRProduct productWithName:[self randomName] sales:sales]];
        }
        
        return [m copy];
    }
    
    + (WRProduct *)randomProductFromTest{
        NSArray *rp = [self testProducts];
        return [rp objectAtIndex:(arc4random()%rp.count)];
    }
    
    + (NSString *)randomName{
        static NSString *alphb = @"abcdefghijklmnopqrstuvwxyz";
        NSUInteger length = arc4random()%10;
        NSMutableString *name = [@"" mutableCopy];
        
        for (int i=0; i<length; i++) {
            NSString *randomLetter = [alphb substringWithRange:NSMakeRange(arc4random()%25, 1)];
            [name appendString:randomLetter];
        }
        
        return [[name copy] capitalizedString];
    }
    
    @end
    

    排序:

    NSArray *products = [WRProductLab testProducts];
    NSLog(@"row products: %@", products);
    
    NSArray *sortedProducts = [products sortedArrayUsingDescriptors:@[
        [NSSortDescriptor sortDescriptorWithKey:@"name"
                                        ascending:YES
                                        selector:@selector(localizedCaseInsensitiveCompare:)],
        [NSSortDescriptor sortDescriptorWithKey:@"sales"
                                        ascending:NO]]];
    NSLog(@"sorted products: %@", sortedProducts);
    
    // 结果:
    /**
    row products: (
        "product name: Bk, sales: 5290",
        "product name: Wid, sales: 1284",
        "product name: Waqb, sales: 9422",
        "product name: Cxwcj, sales: 441",
        "product name: Llt, sales: 2378",
        "product name: Kmgfo, sales: 683",
        "product name: Dqmloqen, sales: 8969",
        "product name: Glxpnqh, sales: 206",
        "product name: Arw, sales: 5866",
        "product name: Kgorl, sales: 7557"
    )
    sorted products: (
        "product name: Arw, sales: 5866",
        "product name: Bk, sales: 5290",
        "product name: Cxwcj, sales: 441",
        "product name: Dqmloqen, sales: 8969",
        "product name: Glxpnqh, sales: 206",
        "product name: Kgorl, sales: 7557",
        "product name: Kmgfo, sales: 683",
        "product name: Llt, sales: 2378",
        "product name: Waqb, sales: 9422",
        "product name: Wid, sales: 1284"
    )
    */
    

    查找search

    查找是信息处理中最常用的操作之一,小到对单个值的查找,大到企业级搜索和搜索引擎,其原理是相通的。

    顺序查找

    顺序查找表示从序列的第一个值开始,直到找到某个特定值,或者直到最后一个值为止。在OC中,有下面这些方法可用于查找:

    • indexOfObject:: 根据给定的值进行查找,通过isEqual:判断是否满足等值关系。
    • indexOfObjectIdenticalTo:: 根据内存地址进行判断是否等值。
    • indexOfObjectPassingTest:: 根据特定条件筛选多个对象,而非单个。
    • indexOfObjectWithOptions:passingTest:: 同上,只是多了一个参数,用来控制遍历行为,可并行或者逆序。
    • filteredArrayUsingPredicate::条件过滤。
    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    // 1. 搜索单个元素, 根据`isEqual:`判断
    NSInteger index = [strs indexOfObject:@"Tim"];
    if (index == NSNotFound) {
        NSLog(@"Have not found 'Tim' in strs");
    } else {
        NSLog(@"find `Tim` at index: %zd", index);
    }
    
    // 根据内存地址判断
    index = [strs indexOfObjectIdenticalTo:@"Tim Cook"];
    NSLog(@"find `Tim Cook` at index: %zd", index);
    
    // 根据条件查找,还可以设置`Range`等条件
    index = [strs indexOfObjectPassingTest:^BOOL(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        return [obj length] < 10;
    }];
    NSLog(@"find the first string(length<10) at index: %zd", index);
    
    // 2. 根据条件查找多个元素
    NSIndexSet *is = [strs indexesOfObjectsWithOptions:NSEnumerationConcurrent passingTest:^BOOL(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj length] > 10) {
            return YES;
        }
        return NO;
    }];
    NSLog(@"find all strings(length>10) at indexset: %@", is);
    
    // 使用NSPredicate
    NSArray *lengthGt10 = [strs filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id  _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
        return [evaluatedObject length] > 10;
    }]];
    NSLog(@"all strings(length>10): %@", lengthGt10);
    
    // results:
    /**
    collections[1885:69544] Have not found 'Tim' in strs
    collections[1885:69544] find `Tim Cook` at index: 1
    ollections[1885:69544] find the first string(length<10) at index: 1
    collections[1885:69544] find all strings(length>10) at indexset: <NSIndexSet: 0x100653b90>[number of indexes: 3 (in 2 ranges), indexes: (0 2-3)]
    collections[1885:69544] all strings(length>10): (
        "John Appleseed",
        "Hair Force One",
        "Michael Jurewitz"
    )
    */
    

    对于上面的Product,先定义isEqual:方法:

    - (BOOL)isEqual:(id)object{
        WRProduct *other = (WRProduct *)object;
        return [self.name isEqualToString:other.name] && self.sales == other.sales;
    }
    

    查找特定product

    NSArray *testProducts = [WRProductLab testProducts];
    WRProduct *randomProduct = [testProducts objectAtIndex:arc4random()%testProducts.count];
    WRProduct *fakeRandomProduct = [WRProduct productWithName:randomProduct.name sales:randomProduct.sales];
    
    // raw random product
    NSLog(@"The index of random product in test products: %zd", [testProducts indexOfObject:randomProduct]);
    NSLog(@"The index of identical random product in test products: %zd", [testProducts indexOfObjectIdenticalTo:randomProduct]);
    
    // the fake random product
    NSLog(@"The index of random product in test products: %zd", [testProducts indexOfObject:fakeRandomProduct]);
    NSLog(@"The index of identical random product in test products: %zd", [testProducts indexOfObjectIdenticalTo:fakeRandomProduct]);
    
    // 结果是前三个为相同的index,最后一个为NSNotFound
    

    二分查找

    二分查找有更快的查找速度indexOfObject:inSortedRange:options:usingComparator:,但是有一个条件:序列必须是经过排序的,否则结果是undefined。

    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    NSLog(@"start search at array: %@", strs);
    
    NSInteger indexBeforeSort = [strs indexOfObject:@"Tim Cook" inSortedRange:NSMakeRange(0, strs.count) options:NSBinarySearchingFirstEqual usingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        return [obj1 compare:obj2];
    }];
    
    strs = [strs sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
    NSInteger indexAfterSort = [strs indexOfObject:@"Tim Cook" inSortedRange:NSMakeRange(0, strs.count) options:NSBinarySearchingFirstEqual usingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        return [obj1 compare:obj2];
    }];
    
    NSLog(@"The index of 'Tim Cook' before sort is %zd, after sort is %zd", indexBeforeSort, indexAfterSort);
    // NSNotFound, 3
    

    枚举enumerate

    快速枚举

    使用for ... in就是快速枚举。

    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
        
    for (NSString *str in strs) {
        NSLog(@"Fast Enumeration, string: %@", str);
    }
    

    下标取值

    即传统的for循环。

    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    for (int idx = 0; idx < strs.count; idx++) {
        id object = strs[idx];
        NSLog(@"For Loop, string: %@", object);
    }
    

    Block

    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    [strs enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"Block, string: %@, stop:%s", obj, stop);
    }];
    

    NSEnumerator

    NSArray *strs = @[@"John Appleseed", @"Tim Cook", @"Hair Force One", @"Michael Jurewitz"];
    
    NSEnumerator *enumerator = [strs objectEnumerator];
    id object = nil;
    while ((object = enumerator.nextObject) != nil) {
        NSLog(@"NSENumerator, string: %@", object);
    }
    

    NSDictionary

    排序sort

    使用细节上和NSArray类似,只不过对于Dictionary的排序,是通过value的值进行比较,而非key。具体方法有:

    • keysSortedByValueUsingSelector:
    • keysSortedByValueUsingComparator:
    • keysSortedByValueWithOptions:usingComparator:

    枚举

    除过前面NSArray中提及的枚举方法,这里再说一种方法。实际上,它主要用于查找,当不设置任何条件时,就成了枚举。更重要的是,它也可以控制枚举行为是否为并行或者逆序。

    // 广义的查找
    [dict keysOfEntriesWithOptions:NSEnumerationConcurrent passingTest:^BOOL(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSLog(@"key: %@, value: %@", key, obj);
        return YES;
    }];
    
    // 用Block枚举
    [dict enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSLog(@"key: %@, value: %@", key, obj);
    }];
    

    NSSet

    除过逻辑含义的不同,set和array与dictionary的使用方法类似,对照理解即可。

    相关文章

      网友评论

          本文标题:Objective-C集合类使用指南

          本文链接:https://www.haomeiwen.com/subject/xedmbktx.html