NSString、NSArray和NSDictionary | 青训营笔记

377 阅读7分钟

这是我参与「第四届青训营 」笔记创作活动的的第3天。
下面介绍NSString、NSArray和NSDictionary数据结构。

NSString 字符串

NSString 是在 Objective-C 语言里最常用的专门处理字符串的类。

严格来说 NSString 并不是 OC 的字符串纯值,OC 的字符串纯值可以使用C语言的 char 形式的纯字符串。而 NSString 它继承自 NSObject,有方法有变量,妥妥的是个类,不过一般开发不太会使用纯字符串,因为它不好跟其他数据交互,而是使用 NSString,所以我们还是把它视为 OC 里的字符串。

// C语言字符串(基础数据类型)
char *cString = "James";

//NSString.h (专门处理字符串的类)
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
//......
@property (readonly) NSUInteger length;
- (unichar)characterAtIndex:(NSUInteger)index;
- (instancetype)init;
//.....
@end

1. 创建

NSString 有许多种的创建方式。

// @"" 创建 NSString 对象 (常用)
NSString *stringObject = @"hello";

// C语言字符串
char *cString = "James";
// C语言字符串 -> NSString 对象
NSString *stringObject1 = [NSString stringWithUTF8String:"James"];

// 初始化一个空字符串
NSString *stringObject2 = [[NSString alloc] init];
// 初始化一个字符串
NSString *stringObject3 = [NSString stringWithString:@"hello"];
// 拼接一个字符串
NSString *stringObject4 = [stringObject3 stringByAppendingString:@" world"];

2. 类型转换

如果要将其他类型单独或组合转换成字符串,可以使用 initWithFormat 初始化方法,使用类似C语言的格式化表达创建,其中常用的格式例如 %d 表达整数,%f 表达浮点数,还有一种 %@ 去获得一个对象的描述。

如果 %@ 是用在 NSString 对象上那就是该字符串的值,如果是用在其他对象上则会获得该对象 description 方法里的回传值,通常是该对象的类和内存地址描述。

如果要将现有的字符串转成基础类型,可以直接使用 NSString 提供的方法转成 int, float, double 等等。

// 创建格式化字符串 - @"123 , str"
int a = 123;
NSString *stringObject5 = [[NSString alloc]initWithFormat:@"%d , %@", a, "str"];

// MARK: NSString转成基础类型
NSString *numberStr = @"123";
// BOOL
BOOL boolValue = [numberStr boolValue];
// int
int intValue = [numberStr intValue];
//float 
float floatValue = [testStr floatValue];
// double
double doubleValue = [testStr doubleValue];

3. 子字符串

NSString *stringObject5 = @"hello";

// 获取字符串的长度
NSUInteger length = [stringObject5 length];
// 获取索引下标的字符
unichar index_char = [stringObject5 characterAtIndex:0];

// 截取字符串,从索引位置到结尾
NSString *subStr1 = [stringObject5 substringFromIndex:1];
// 截取字符串,从开始到索引位置
NSString *subStr2 = [stringObject5 substringToIndex:3];
// 截取字符串,从索引开始,取长度个数组成的字符串
NSRange range = NSMakeRange(1, 2);
NSString *subStr3 = [stringObject5 substringWithRange:range];

NSArray 数组

NSArrayNSString 一样是 NSObject 子类,NSArray 也不是纯值类型,所以具备了许多实现好的数组访问方法。

NSString 一样,创建 NSArray 也有简写,使用 @中括号 就相当于创建一个 NSArray 对象。

需要注意的是:NSArray 中不能存放 int, float, bool 等基本类型,只能保存 Objective-C 对象,但是一个数组对象里面可以保存不同 Class 的对象,因为每个 Objective-C 对象实际是一个指针,所以从指针的这个角度来看的话,数组存放的每一项不管是什么类的对象都是等长的。这样处理比如 lengthgetadd 等操作的时候就非常简单了。

如果想保存基本类型可以通过 NSNumber 这个类去封装后在放进数组里。

// 空数组
NSArray *arr1 = [NSArray array];
// 效果同上
NSArray *arr2 = [[NSArray alloc] init];

// @[...] = NSArray对象创建
NSArray *arr3 = @[@"iOS", @"Android", @"Server"];
// 效果同上
NSArray *arr4 = [NSArray arrayWithObjects:@"iOS", @"Android", @"Server"];

// 如果需要放基本类型,可使用NSNumber or @(value) = 自动转成NSNumber对象
NSNumber *numberObject = [[NSNumber alloc] initWithInt:100];
NSArray *arr4 = [NSArray arrayWithObjects:@(123), numberObject];

// 限制存放对象为NSNumer
NSArray<NSNumber *> *numberArrayObject = @[@(1), @(-1), @(3.3)];

1. 查询

需要特别注意的是,数组的查询都是针对对象指针的,也就是如果你拿另外一个虽然是同样内容的字符串查询,但因为不是同个对象,也会有查询不到的情况。

NSString *str = @"bytedance";
NSArray *array = @[str, @"iOS", @"Android"];

// count  数组中所含元素个数
NSLog(@"count = %d", array.count);
// 返回对应位置对象
NSObject *obj1 = array[0]
// 返回元素对应位置(认对象)
NSUInteger index = [array indexOfObject:obj1];
// lastObject  返回数组最后一个元素
NSObject *obj2 = [array lastObject];

// containsObject   是否包含指定对象
if ([array containsObject:str]) {
    // true
}
if ([array containsObject:@"iOS"]) {
    // true
}

//使用for循环遍历
for (NSString *strObj in array) {
    NSLog(@"%@", strObj);
}

2. 不可变数组 NSArray vs. 可变数组 NSMutableArray

NSArray 数组,在设计上并不能添加或删除树组里面的元素,也就是这个 NSArray 对象一旦被创建了就没法修改。

如果需要一个可以增删的数组,就必须使用 NSArray 的子类 NSMutableArray,只有 NSMutableArray 才有声明增删方法。

除了用法上的区别之外,实际上 NSArray 的底层实现与 NSMutableArray 的底层实现区别非常大,NSArray 的查找性能也确实优于 NSMutableArray

 @interface NSMutableArray<ObjectType> : NSArray<ObjectType>
// 以下方法NSArray无
- (void)addObject:(ObjectType)anObject;
- (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
@end
// 可变
NSMubaleArray *arr1 = .....
doSomething(NSMubaleArray* array);
// arr1 是否已经被修改

// 不可变
NSArray *arr2 = .....
doSomething(NSArray* array);
// arr2 还是原数据
  • NSArray
    • 对象数据可能被修改
    • 线程不安全
    • 查找性能较差
    • 一般用作方法内的临时变量,可做增删
  • NSMutableArray
    • 传进去的对象不会被修改
    • 线程安全
    • 查找性能好
    • 用于属性声明或是方法参数/回传值
// NSArray -> NSMutableArray
NSMutableArray *mutableArr1 = [[NSMutableArray alloc] initWithArray:numberArrayObject];
// NSMutableArray -> NSArray
NSArray *fixArr1 = [[NSArray alloc] initWithArray:mutableArr1];
@end

3. NSMutableArray 数据增删

如果在声明数组时没有限制数组的存放类型,那么任何对象都能被新增到数组里。

// NSMutableArray 数组增删 NSMutableArray *mutableArrayObject = [[NSMutableArray alloc] init];
// 新增任意对象
[mutableArrayObject addObject:@"StringObject1"];
// 在指定下标添加对象
[mutableArrayObject insertObject:@(0) atIndex:0];
// 批量添加
[mutableArrayObject addObjectsFromArray:@[@"StringObject2", @(1)]];

// 移除末尾
[mutableArrayObject removeLastObject];
// 删除指定下标
[mutableArrayObject removeObjectAtIndex:0];
// 移除全部
[mutableArrayObject removeAllObjects];

NSDictionary 字典

字典是一个基于 Key-Value 键值对访问的数据类型。跟字符串和数组类似,使用 @花括号 就可以快速的创建字典对象。

我们一般使用 NSString 当做字典的 key 做访问,而 value 跟数组一样必须是个 Objective-C 的对象,如果要保存基础类型一样可以先转成 NSNumber

NSDictionary 本身也是个 OC 对象,所以字典的 value 也可以是字典对象。

// @{} = 创建NSDictionary对象
NSDictionary *dict1 = @{
    @"key1":@"value1"
};
// 效果同上
NSDictionary *dict2 = [NSDictionary dictionaryWithObject:@"key1" forKey:@"value1"];

// 多Key
NSDictionary *dict1 = @{
    @"key1":@"value1",
    @"key2":@"value2"
};
//效果同上
NSDictionary *dict5 = [[NSDictionary alloc]initWithObjects:@[@"value1",@"value2"] 
                                                   forKeys:@[@"key1",@"key2"]];
// 指定保存类型   
NSDictionary<NSString*, NSNumber*> *dictionaryObject = @{
    @"numberKey1": @(0),
    @"numberKey2": @(1),
}                                                                  

1. 访问

NSDictionary 字典的访问方法跟 NSArray 数组类似,不过把访问的下标从整数替换成了字符串。

如果这个字典在声明时没有加上类型限制(value 是什么类型),那不管使用 key 下标直接访问或是通过 objectForKey 方法访问,返回的都是 id 类型,也就是可能是任意类型的对象,在调用这个从字典里取出来的对象方法时,谨慎一点都要先判断过类型,否则如果这对象不是我们预期的类型,我们还去调用方法,就会导致崩溃。

NSDictionary *dictionary1 = @{
    @"key1": @"value1"
};

// count  (字典元素个数   key:value 是一一对应的,所以是算作一个的)
int dictionary1Count = [dictionary1 count];

// objectForKey:  (获取一个key对应的值)
NSObject *getValue = [dictionary1 objectForKey:@"key1"];
// 等同
id getValueDirect = dictionary1[@"key1"];

// allKeys   (获取key集合)
NSArray *keysArr = [dictionary1 allKeys];

//allValues   (获取所有value)
NSArray *valuesArr = [dictionary1 allValues];

// 遍历所有Key,并访问对应Value
[dictionary1 enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
     NSLog(@"key = %@  value = %@",key,[dictionary1 objectForKey:key]);
}];

2. NSMutableDictionary 增删

字典也分为不可变字典 NSDictionary 以及可变字典 NSMutableDictionary。如果在声明字典对象时没有指定任何类型,那这个字典就可以添加任何的 NSObject 子类对象。

NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc] initWithDictionary:@{
  @"key1": @"value1"
}];

//setObject:(setValue:)forKey:   (设置某Key对应值)
[mutableDictionary setObject:@"value2" forKey:@"key2"];
[mutableDictionary setValue:@"value3" forKey:@"key3"];
mutableDictionary[@"key4"] = @"value4";

//removeObjectForKey:   (移除某一个key对应的元素)
[mutableDictionary removeObjectForKey:@"key2"];
//removeAllObjects  (移除所有元素)
[mutableDictionary removeAllObjects];