本文整理一些Objective-C中常用的数据结构,特别是一些特殊用法的注意点,如自定义类型作为NSMutableDictionary的key;默认情况下容器都是持有容器元素的引用计数的,后面也会介绍NSMapTable如何支持弱引用。

NSString

初始化

NSString * str = @"hello";
NSString * str2 = str;

与C++的std::string相互转换

NSString * str = @"中国";

// NSString转为utf-8
std::string cstr = [str UTF8String];

// utf-8转为NSString
NSString * str2 = [NSString stringWithUTF8String:cstr.c_str()];

格式化

格式化串,支持类似printf的控制符,以及%@来支持Objective-C的对象类型

NSString *string1 = [NSString stringWithFormat:@"A string: %@, a float: %1.2f",
                                               @"string", 31415.9265];
关于%@
如果对象实现了descriptionWithLocale,则会发该消息;否则,返回description对应的值
关于%s
格式化c字符串,由于格式化时会使用系统默认编码格式,结果不可控,尽量不要使用,用%@和NSString来代替

其他常用方法

@property (readonly) NSUInteger length;
- (unichar)characterAtIndex:(NSUInteger)index;

- (NSString *)substringFromIndex:(NSUInteger)from;
- (NSString *)substringToIndex:(NSUInteger)to;
- (NSString *)substringWithRange:(NSRange)range;

- (NSComparisonResult)compare:(NSString *)string;
- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask;
- (BOOL)isEqualToString:(NSString *)aString;

- (BOOL)hasPrefix:(NSString *)aString;
- (BOOL)hasSuffix:(NSString *)aString;
- (BOOL)containsString:(NSString *)aString;

- (NSString *)stringByAppendingString:(NSString *)aString;

数组 NSArray & NSMutableArray

NSArray

NSArray是不可变数组类型,对数组的变更方法操作都会返回新的数组

// count属性
@property (readonly) NSUInteger count;

// 实例方法
- (id)objectAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithObjects:(const id [])objects count:(NSUInteger)cnt 

- (NSArray *)arrayByAddingObject:(id)anObject;
- (NSArray *)arrayByAddingObjectsFromArray:(NSArray *)otherArray;

- (BOOL)containsObject:(id)anObject;
- (NSUInteger)indexOfObject:(id)anObject;
- (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range;
- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject;
- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(NSRange)range;

- (instancetype)initWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
- (instancetype)initWithArray:(NSArray *)array;
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;

// 类方法
+ (instancetype)array;
+ (instancetype)arrayWithObject:(id)anObject;
+ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
+ (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
+ (instancetype)arrayWithArray:(NSArray *)array;

NSMutableArray

NSMutableArray继承自NSArray,因此接受NSArray作为参数的方法,都可以使用NSMutableArray

常用的方法:

@interface NSMutableArray : NSArray

- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCapacity:(NSUInteger)numItems NS_DESIGNATED_INITIALIZER;

@end

@interface NSMutableArray (NSExtendedMutableArray)
    
- (void)addObjectsFromArray:(NSArray *)otherArray;
- (void)removeAllObjects;
- (void)removeObject:(id)anObject inRange:(NSRange)range;
- (void)removeObject:(id)anObject;
- (void)removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range;
- (void)removeObjectIdenticalTo:(id)anObject;
- (void)removeObjectsInArray:(NSArray *)otherArray;
- (void)removeObjectsInRange:(NSRange)range;
- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray;
- (void)setArray:(NSArray *)otherArray;
- (void)sortUsingFunction:(NSInteger (*)(id, id, void *))compare context:(void *)context;
- (void)sortUsingSelector:(SEL)comparator;

- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes;
- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray *)objects;

- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx NS_AVAILABLE(10_8, 6_0);

- (void)sortUsingComparator:(NSComparator)cmptr NS_AVAILABLE(10_6, 4_0);

@end

集合 NSSet & NSMutableSet

字典 NSDictionary & NSMutableDictionary & NSMapTable

字典是常用的支持键值对查询的容器。

自定义结构如何支持NSMutableDictionary作为key类型

NSMutableDictionary一般使用NSString作为key,但是如果需要支持自定义的类型作为key,需要实现几个方法:

例子:

// interface
@interface PluginInstance : NSObject <NSCopying>

@property (retain) NSString * pluginId;
@property (retain) NSString * instanceId;

- (id)initWithPluginId:(NSString*)pluginId
			instanceId:(NSString*)instanceId;

@end

// implementation
@implementation PluginInstance

- (id)initWithPluginId:(NSString*)pluginId
			instanceId:(NSString*)instanceId
{
	self = [super init];
	if (self) {
		self.pluginId = pluginId;
		self.instanceId = instanceId;
	}
	return self;
}

// hash
- (NSUInteger)hash
{
	return [self.pluginId hash] + 7 * [self.instanceId hash];
}

- (BOOL)isEqual:(id)object
{
	if (![object isKindOfClass:[self class]])
		return NO;
	
	PluginInstance * other = (PluginInstance *)object;
	return [self.pluginId isEqualToString:other.pluginId] && [self.instanceId isEqualToString:other.instanceId];
}

- (id)copyWithZone:(NSZone *)zone
{
	PluginInstance *objectCopy = [[PluginInstance allocWithZone:zone] init];
	objectCopy.pluginId = [NSString stringWithString:self.pluginId];
	objectCopy.instanceId = [NSString stringWithString:self.instanceId];
	return objectCopy;
}

@end

// test
PluginInstance * pluginInstance = [[PluginInstance alloc] initWithPluginId:@"bench" instanceId:@"1234"];
		
		NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
		dict[pluginInstance] = @"hello world";

NSMapTable支持自定义引用计数的Map

NSMapTable是对NSMutableDictionary的补充,因为NSMutableDictionary对于key和value都是持有引用计数的,而NSMapTable可以指定key或者value是strong强引用计数还是weak弱引用计数。

例如:

// value是弱引用计数的Map
NSMapTable * weakWebviews = [NSMapTable strongToWeakObjectsMapTable];