iOS 集合存取结构体变量

最近开发中遇到一个需求,需将结构体变量存入一个集合中,并从集合中取出。实现思路上,由于结构体是C类型的数据结构,我们应当先通过NSValue对结构体变量进行装箱操作,之后将装箱后的NSValue对象存入集合,最后再从集合内获取NSValue对象并将其拆箱为结构体变量。以存取CGColorRef变量为例,具体实现如下:
首先,对结构体变量进行装箱及拆箱的处理。查看系统UIGeometry.h文件,我们可以发现对于一些结构体,系统已进行了装箱及拆箱的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface NSValue (NSValueUIGeometryExtensions)

+ (NSValue *)valueWithCGPoint:(CGPoint)point;
+ (NSValue *)valueWithCGVector:(CGVector)vector;
+ (NSValue *)valueWithCGSize:(CGSize)size;
+ (NSValue *)valueWithCGRect:(CGRect)rect;
+ (NSValue *)valueWithCGAffineTransform:(CGAffineTransform)transform;
+ (NSValue *)valueWithUIEdgeInsets:(UIEdgeInsets)insets;
+ (NSValue *)valueWithUIOffset:(UIOffset)insets NS_AVAILABLE_IOS(5_0);

- (CGPoint)CGPointValue;
- (CGVector)CGVectorValue;
- (CGSize)CGSizeValue;
- (CGRect)CGRectValue;
- (CGAffineTransform)CGAffineTransformValue;
- (UIEdgeInsets)UIEdgeInsetsValue;
- (UIOffset)UIOffsetValue NS_AVAILABLE_IOS(5_0);

@end

因而,我们可以参照系统实现,对所需结构体变量进行处理。查阅文档,我们发现NSValue提供了两个API,valueWithBytes:objCType:getValue:方便我们实现:

1
2
3
4
5
Creates a value object containing the specified value, interpreted with the specified Objective-C type.
+ (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;

Copies the value into the specified buffer.
- (void)getValue:(void *)buffer;

CGColorRef变量装箱及拆箱实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation NSValue (Boxing)

+ (NSValue *)valueWithCGColor:(CGColorRef)color
{
return [NSValue valueWithBytes:&color objCType:@encode(CGColorRef)];
}

- (CGColorRef)CGColorValue
{
CGColorRef color;
[self getValue:&color];
return color;
}

@end

这里,我们通过NSValue的分类方法形式实现。需要注意的是,编译器指令@encode()表示返回一种类型的C字符串,类似于C的typeof操作。装箱及拆箱实现后,我们可以根据常用集合类型对结构体变量进行存取操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NSValue *testColorValue = [NSValue valueWithCGColor:[UIColor cyanColor].CGColor];

// Dictionary
NSMutableDictionary *testDictionary = [NSMutableDictionary dictionary];

[testDictionary setValue:testColorValue forKey:@"testColorValue"];
[[testDictionary valueForKey:@"testColorValue"] CGColorValue];

// Array
NSMutableArray *testArray = [NSMutableArray array];

[testArray addObject:testColorValue];
[[testArray objectAtIndex:0] CGColorValue];

// Set
NSMutableSet *testSet = [NSMutableSet set];

[testSet addObject:testColorValue];
[[testSet anyObject] CGColorValue];

以上实现主要在外部进行结构体变量装箱及拆箱操作,为方便代码管理,我们可以在此基础上进一步优化,为特定类型结构体的存取进行接口设计,将装箱及拆箱操作置于实现内部,外部只需传入结构体变量便能完成存取操作。以存取CGColorRef变量为例:
字典存取CGColorRef变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@implementation NSDictionary (Store)

// 根据Key值,获取CGColorRef变量,若为空返回NULL
- (CGColorRef)CGColorForKey:(NSString *)key
{
return [self CGColorForKey:key default:NULL];
}

// 根据Key值,获取CGColorRef变量,若为空返回默认值
- (CGColorRef)CGColorForKey:(NSString *)key default:(CGColorRef)defaultValue
{
if (!key) return defaultValue;
id value = self[key];
if (value && [value isKindOfClass:[NSValue class]]) return [value CGColorValue];

return defaultValue;
}

@end



@implementation NSMutableDictionary (Store)

// 添加CGColorRef变量
- (void)setCGColor:(CGColorRef)color forKey:(NSString *)key
{
[self setValue:[NSValue valueWithCGColor:color] forKey:key];
}

@end

数组存取CGColorRef变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@implementation NSArray (Store)

// 获取index对应的CGColorRef变量,若为空返回NULL
- (CGColorRef)CGColorAtIndex:(NSUInteger)index
{
id color = [self objectAtIndex:index];
if (color && [color isKindOfClass:[NSValue class]]) return [color CGColorValue];

return NULL;
}

@end



@implementation NSMutableArray (Store)

// 添加CGColorRef变量
- (void)addCGColor:(CGColorRef)color
{
[self addObject:[NSValue valueWithCGColor:color]];
}

// 根据index,插入CGColorRef变量
- (void)insertCGColor:(CGColorRef)color atIndex:(NSUInteger)index
{
[self insertObject:[NSValue valueWithCGColor:color] atIndex:index];
}

// 将index对应的对象,替换为CGColorRef变量
- (void)replaceObjectAtIndex:(NSUInteger)index withCGColor:(CGColorRef)color
{
[self replaceObjectAtIndex:index withObject:[NSValue valueWithCGColor:color]];
}

@end

Set存取CGColorRef变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 是否包含特定CGColorRef变量,有则返回该变量,否则返回NULL
- (CGColorRef)CGColorMember:(CGColorRef)color
{
id aColor = [self member:[NSValue valueWithCGColor:color]];
if (aColor || [aColor isKindOfClass:[NSValue class]]) return [aColor CGColorValue];

return NULL;
}

// 获取任一个CGColorRef变量,若为空返回NULL
- (CGColorRef)anyCGColor
{
id color = [self anyObject];
if (color || [color isKindOfClass:[NSValue class]]) return [color CGColorValue];

return NULL;
}

// 是否包含特定的CGColorRef变量
- (BOOL)containsCGColor:(CGColorRef)color
{
return [self containsObject:[NSValue valueWithCGColor:color]];
}

@end



@implementation NSMutableSet (Store)

// 添加CGColorRef变量
- (void)addCGColor:(CGColorRef)color
{
[self addObject:[NSValue valueWithCGColor:color]];
}

// 移除特定的CGColorRef变量
- (void)removeCGColor:(CGColorRef)color
{
[self removeObject:[NSValue valueWithCGColor:color]];
}

@end

存取时,如下操作即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Test Datas
CGColorRef testRed = [UIColor redColor].CGColor;

// Dictionary
NSMutableDictionary *testDictionary = [NSMutableDictionary dictionary];
[testDictionary setCGColor:testRed forKey:@"textRed"];
[testDictionary CGColorForKey:@"textRed"];

// Array
NSMutableArray *testArray = [NSMutableArray array];
[testArray addCGColor:testRed];
[testArray CGColorAtIndex:0];

// Set
NSMutableSet *testSet = [NSMutableSet set];
[testSet addCGColor:testRed];
[testSet anyCGColor];

至此,实现基本完成。对于其它结构体或自定义结构体变量,原理及实现方式类同,可根据自身业务需求进行扩展。

示例代码:https://github.com/ColinHwang/Demo-of-Tutorial-in-Blog/tree/master/iOS-StoreStructuresInCollections

参考资料:

[1]Mattt Thompson.NSValue[EB/OL].http://nshipster.com/nsvalue ,2013-1-28.
[2]Mattt Thompson.Type Encodings[EB/OL].http://nshipster.com/type-encodings ,2013-2-4.
[3]John Muchow.Storing CGPoint, CGSize and CGRect in Collections using NSValue[EB/OL].http://iosdevelopertips.com/cocoa/storing-cgpoint-cgsize-and-cgrect-in-collections-using-nsvalue.html ,2013-12-19.
[4]Apple Inc.NSValue Reference Class[EB/OL].https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSValue_Class/index.html ,2016-8-25.