容器

iOS容器类介绍

2019-05-04  本文已影响0人  Corbin___

开发中最常用的是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'
上一篇 下一篇

猜你喜欢

热点阅读