美文网首页容器
iOS容器类介绍

iOS容器类介绍

作者: Corbin___ | 来源:发表于2019-05-04 17:22 被阅读0次

    开发中最常用的是NSArray和NSDictionary,这就不介绍了

    弱引用容器 强引用容器
    NSHashTable NSMutableSet
    NSMapTable NSMutableDictionary
    NSPointerArray NSMutableArray

    弱引用容器在添加对象的性能方面都弱于对应的强引用容器

    NSArray 和 NSSet的区别

    NSSet和NSArray都是存储对象,都属于集合。
    NSSet是无序的,也就是在内存上的存储方式是不连续的,NSArray是有序的

    验证有序和无序,可以%p,打印元素地址,看看是否连续

    NSSet和我们常用NSArry区别是:
    搜索一个一个元素时NSSet比NSArray效率高,主要是它用到了一个算法hash

    比如你要存储元素A,一个hash算法直接就能直接找到A应该存储的位置;同样,当你要访问A时,一个hash过程就能找到A存储的位置。而对于NSArray,若想知道A到底在不在数组中,则需要遍历整个数组,显然效率较低了;

    个人猜想部分:
    集合已有元素A,现在添加B,如果B的hash值跟A不同,那么就直接添加B进入,如果B的hash值跟A相同,那么A元素会调用- (BOOL)isEqual:(id)object,object就是B对象,如果return YES,那么就代表A和B是重复元素,B添加不进去,如果不同,B可以添加进去
    NSHashTable也是这样

    验证猜想实验:

    #pragma mark - *********  A  ********
    
    @interface A : NSObject
    
    @end
    
    @implementation A
    
    - (NSUInteger)hash
    {
        return 1;
    }
    
    - (BOOL)isEqual:(id)object
    {
        return YES;
    }
    
    @end
    
    #pragma mark - *********  ViewController  ********
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        A *a1 = [[A alloc] init];
        A *a2 = [[A alloc] init];
        A *a3 = [[A alloc] init];
    
        NSMutableSet *set = [[NSMutableSet alloc] init];
        [set addObject:a1];
    /*
    当添加a2的时候,会调a2的- (NSUInteger)hash,
    如果hash返回值跟a1一样,那么就会触发a1的- (BOOL)isEqual:(id)object,
    如果这时候返回YES,那么a2就添加不进去
    */
        [set addObject:a2];
    }
    
    @end
    

    NSSet.h

    #import <Foundation/NSObject.h>
    #import <Foundation/NSEnumerator.h>
    
    @class NSArray, NSDictionary, NSString;
    
    /****************   Immutable Set   ****************/
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface NSSet<__covariant ObjectType> : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
    
    /// set元素数量
    @property (readonly) NSUInteger count;
    /// 根据元素的hash去容器中查找object,如果有就返回,没有返回nil
    - (nullable ObjectType)member:(ObjectType)object;
    /// 返回NSEnumerator对象
    - (NSEnumerator<ObjectType> *)objectEnumerator;
    /// NS_DESIGNATED_INITIALIZER就是表明这是一个全能初始化方法
    - (instancetype)init NS_DESIGNATED_INITIALIZER;
    - (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects count:(NSUInteger)cnt NS_DESIGNATED_INITIALIZER;
    - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
    
    @end
    
    @interface NSSet<ObjectType> (NSExtendedSet)
    /// 将set中的所有元素装进数组返回
    @property (readonly, copy) NSArray<ObjectType> *allObjects;
    /// 返回一个便捷的元素(我也不太理解)
    - (nullable ObjectType)anyObject;
    /// 是否包含anObject
    - (BOOL)containsObject:(ObjectType)anObject;
    /// 以字符串的形式返回set元素,如
    {(
        <A: 0x666666666666>,
        2
    )}
    @property (readonly, copy) NSString *description;
    - (NSString *)descriptionWithLocale:(nullable id)locale;
    /// 判断两个集合是否有交集
    - (BOOL)intersectsSet:(NSSet<ObjectType> *)otherSet;
    /// 判断两个集合中的元素是否完全一样
    - (BOOL)isEqualToSet:(NSSet<ObjectType> *)otherSet;
    /// 判断集合A是否是集合B的子集
    - (BOOL)isSubsetOfSet:(NSSet<ObjectType> *)otherSet;
    /// 让集合中的所有元素调用selector
    - (void)makeObjectsPerformSelector:(SEL)aSelector NS_SWIFT_UNAVAILABLE("Use enumerateObjectsUsingBlock: or a for loop instead");
    - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(nullable id)argument NS_SWIFT_UNAVAILABLE("Use enumerateObjectsUsingBlock: or a for loop instead");
    
    /// 添加元素
    - (NSSet<ObjectType> *)setByAddingObject:(ObjectType)anObject API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    /// 添加集合
    - (NSSet<ObjectType> *)setByAddingObjectsFromSet:(NSSet<ObjectType> *)other API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    /// 添加数组到集合中
    - (NSSet<ObjectType> *)setByAddingObjectsFromArray:(NSArray<ObjectType> *)other API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
    
    /// 遍历
    - (void)enumerateObjectsUsingBlock:(void (NS_NOESCAPE ^)(ObjectType obj, BOOL *stop))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
    - (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (NS_NOESCAPE ^)(ObjectType obj, BOOL *stop))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
    ///////////////////////////
    NSSet *set1 = [[NSSet alloc] initWithObjects:a1, @"2", nil];
        NSSet *set = [set1 objectsPassingTest:^BOOL(id  _Nonnull obj, BOOL * _Nonnull stop) {
            if ([obj isKindOfClass:[NSString class]] && [obj isEqualToString:@"2"]) {
                return YES;
            } else {
                return NO;
            }
        }];
        // set 元素只有2
    //////////////////////////////////////
    /// 过滤元素,返回一个新的集合
    - (NSSet<ObjectType> *)objectsPassingTest:(BOOL (NS_NOESCAPE ^)(ObjectType obj, BOOL *stop))predicate API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
    //////////////////////
    typedef NS_OPTIONS(NSUInteger, NSSortOptions) {
        NSSortConcurrent = (1UL << 0), // 并行
        NSSortStable = (1UL << 4), // 串行
    };
    // 可以选择并行遍历还是串行遍历
    - (NSSet<ObjectType> *)objectsWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (NS_NOESCAPE ^)(ObjectType obj, BOOL *stop))predicate API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
    
    @end
    
    @interface NSSet<ObjectType> (NSSetCreation)
    
    + (instancetype)set;
    + (instancetype)setWithObject:(ObjectType)object;
    + (instancetype)setWithObjects:(const ObjectType _Nonnull [_Nonnull])objects count:(NSUInteger)cnt;
    + (instancetype)setWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
    + (instancetype)setWithSet:(NSSet<ObjectType> *)set;
    + (instancetype)setWithArray:(NSArray<ObjectType> *)array;
    
    - (instancetype)initWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
    - (instancetype)initWithSet:(NSSet<ObjectType> *)set;
    - (instancetype)initWithSet:(NSSet<ObjectType> *)set copyItems:(BOOL)flag;
    - (instancetype)initWithArray:(NSArray<ObjectType> *)array;
    
    @end
    
    /****************   Mutable Set ****************/
    
    @interface NSMutableSet<ObjectType> : NSSet<ObjectType>
    
    - (void)addObject:(ObjectType)object;
    - (void)removeObject:(ObjectType)object;
    - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
    - (instancetype)init NS_DESIGNATED_INITIALIZER;
    - (instancetype)initWithCapacity:(NSUInteger)numItems NS_DESIGNATED_INITIALIZER;
    
    @end
    
    @interface NSMutableSet<ObjectType> (NSExtendedMutableSet)
    
    - (void)addObjectsFromArray:(NSArray<ObjectType> *)array;
    - (void)intersectSet:(NSSet<ObjectType> *)otherSet;
    /// 删除set1中与set2相同的元素
    - (void)minusSet:(NSSet<ObjectType> *)otherSet;
    - (void)removeAllObjects;
    /// 取并集
    - (void)unionSet:(NSSet<ObjectType> *)otherSet;
    /// 将otherSet替换了set并且将两个集合的交集加到set中
    - (void)setSet:(NSSet<ObjectType> *)otherSet;
    
    @end
    
    @interface NSMutableSet<ObjectType> (NSMutableSetCreation)
    
    + (instancetype)setWithCapacity:(NSUInteger)numItems;
    
    @end
    
    /****************   Counted Set ****************/
    /// 每个插入到NSCountedSet对象中的不同对象都有一个与之关联的计数器。
    @interface NSCountedSet<ObjectType> : NSMutableSet<ObjectType> {
        @private
        id _table;
        void *_reserved;
    }
    
    - (instancetype)initWithCapacity:(NSUInteger)numItems NS_DESIGNATED_INITIALIZER;
    
    - (instancetype)initWithArray:(NSArray<ObjectType> *)array;
    - (instancetype)initWithSet:(NSSet<ObjectType> *)set;
    
    - (NSUInteger)countForObject:(ObjectType)object;
    
    - (NSEnumerator<ObjectType> *)objectEnumerator;
    - (void)addObject:(ObjectType)object;
    - (void)removeObject:(ObjectType)object;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    NSHashTable

    NSHashTable和NSSet的结构是一样的,都是hash集合,他们的区别在于,NSHashTable可以弱引用元素,NSHashTable在添加对象的性能比NSSet慢

     NSHashTable *hashTable = [[NSHashTable alloc] init];
        NSLog(@"NSHashTable开始添加数据");
        for (long long i = 0; i < 500000000; i++) {
            [hashTable addObject:@1];
        }
        NSLog(@"NSHashTable结束添加数据");
        
        NSLog(@"NSMutableSet开始添加数据");
    
        NSMutableSet *mset = [[NSMutableSet alloc] init];
        for (long long i = 0; i < 500000000; i++) {
            [mset addObject:@1];
        }
        
        NSLog(@"NSMutableSet结束添加数据");
    
    /////////////////////////////
    2019-05-04 13:30:08.851014+0800 Test[23016:3818874] NSHashTable开始添加数据
    2019-05-04 13:30:37.140182+0800 Test[23016:3818874] NSHashTable结束添加数据
    2019-05-04 13:30:37.140439+0800 Test[23016:3818874] NSMutableSet开始添加数据
    2019-05-04 13:31:03.447793+0800 Test[23016:3818874] NSMutableSet结束添加数据
    
    
    typedef NS_OPTIONS(NSUInteger, NSPointerFunctionsOptions) {
        // 内存选项是互斥的
        
        // 默认是强引用
        NSPointerFunctionsStrongMemory // 强引用
        NSPointerFunctionsZeroingWeakMemory // 废弃
        NSPointerFunctionsOpaqueMemory // 移除元素不做任何操作
        NSPointerFunctionsMallocMemory // 去除时调用free() , 加入时calloc()   
        NSPointerFunctionsMachVirtualMemory 
        NSPointerFunctionsWeakMemory // 弱引用
        
        // Personalities 是互斥的
        // default is NSPointerFunctionsObjectPersonality.
        /* 添加对象判断是否重复元素,是根据hash ,isEqual,
        重写元素的description方法,打印集合的时候,会把description的内容打印出来
    */
        NSPointerFunctionsObjectPersonality   
    /*
    // use shifted pointer hash and direct equality
    使用NSPointerFunctionsOpaquePersonality是不会走元素的hash和isEqual方法,说明判断是否重复元素不是用这个,
    按照官方翻译就是使用偏移后指针,进行hash和直接比较等同性;
    我个人的理解,应该就是直接判断这个元素指针的内存地址,如果内存地址一样,肯定是重复元素了,
    但是他说的是偏移指针,难道是对内存地址进行偏移,
    感觉没有必要?这块可以讨论讨论
    */
        NSPointerFunctionsOpaquePersonality 
        NSPointerFunctionsObjectPointerPersonality // 同上,只不过这个多了可以重写description
        NSPointerFunctionsCStringPersonality   // use a string hash and strcmp, description assumes UTF-8 contents; recommended for UTF-8 (or ASCII, which is a subset) only cstrings
        NSPointerFunctionsStructPersonality   // use a memory hash and memcmp (using size function you must set)
        NSPointerFunctionsIntegerPersonality  // use unshifted value as hash & equality
    
        NSPointerFunctionsCopyIn  // the memory acquire function will be asked to allocate and copy items on input
    };
    

    NSMapTable

    NSMapTable和NSDictionary都是字典

    NSDictionary是通过key-value来储存对象的,查找value是通过key来查找的,这意味着放到NSDictionary中的key是copy进来的,为了防止外部改变,导致key值跟着改变了,key可以为对象,但是我们常用的是用字符串,因为如果我们用一个对象,那么copy也是需要消耗时间的

    对比NSMapTable, NSDictionary是键到对象的映射,而NSMapTable是对象到对象的迎神,这就是他们的区别

    NSMapTable的初始化跟NSHashTable一样,也是用NSPointerFunctionsOptions选择储存策略

    NSPointerArray

    一个弱引用元素的数组结构的容器
    这个可以添加任何指针

    - (void)addPointer:(nullable void *)pointer;  // add pointer at index 'count'
    

    相关文章

      网友评论

        本文标题:iOS容器类介绍

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