开发中最常用的是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'
网友评论