Foundation框架中最常用的类。
Foundation框架中最我们经常用的类大致有NSString、NSArray、NSDictionary等等,虽然不需要将每一个类中提供了什么方法都记下来,但是需要做到心中有数,知道Foundation框架提供了什么样的方法,能够达到什么效果,用到的时候直接去API中查找即可,避免因为不知道有这些方法,而走弯路。
NSString
NSString创建与内存存储
NSString是1个数据类型,用来保存OC字符串,NSString的本质是1个类,既然是1个类,所以,最标准的创建NSString对象的方式如下:
NSString *str1 = [NSString new];
NSString *str2 = [[NSString alloc] init];
NSString *str3 = [NSString string];
使用这种方式创建的字符串是1个空的字符@""
。
NSString是OC中最常用的1个类了,所以OC提供了一种更为快速的创建字符串对象的方式:使用前缀@
@"jack";
本质上这是1个NSString对象,这个NSString对象中存储的是"jack"
这个字符串.
NSString *str1 = @"rose";
- @"rose"本质上是1个NSString对象,这个对象中存储的是字符串"rose"。
- 将这个字符串对象的地址返回赋值给str1指针。
格式控制符
%p: 打印指针变量的值即地址。
%@: 打印指针指向的对象。
NSString的恒定性
当我们使用@简要的创建字符串对象的时候,也就是使用1个OC字符串常量来初始化字符串指针的时候,这个字符串对象是存储在常量区(数据段) 的。当我们调用NSString的类方法来创建对象的时候.创建的字符串对象是存储在堆区。
NSString *str = @"jack";
NSString *str1 = [NSString new];
NSLog(@"str:%p",str);
NSLog(@"str1:%p",str1);
上图中可以看出,str和str1字符串打印的地址差别很大,因为str在指向常量区地址,而str1指向堆区的地址。
当在内存中创建1个字符串对象以后,这个字符串对象的内容就无法更改,当我们重新为字符串指针初始化值的时候,并不是修改原来的字符串对象,而是重新的创建1个字符串对象并将这个字符串对象的地址重新复制给字符串指针变量。
NSString *str1 = @"jack";
NSLog(@"str1 = %p",str1);
str1 = @"rose";
NSLog(@"str1 = %p",str1);
从中可以看出,系统在常量区重新创建了字符串"rose",并将"rose"的地址赋值给str1。
当系统准备要在内存中创建字符串对象的时候,会先检查内存中是否有相同内容的字符串对象,如果有,直接指向该内存区域,如果没有才会重新创建。
NSString *str1 = @"jack";
NSLog(@"str1 = %p",str1);
str1 = nil;
NSString *str2 = @"jack";
NSLog(@"str2 = %p",str2);
注意:存储在常量区的数据不会被回收. 所以存储在常量区的字符串对象也不会被回收.
NSString类的常用方法
- 使用拼接的方式创建1个NSString对象。
+ (instancetype)stringWithFormat:(NSString *)format, ... - 得到字符串的长度。
@property (readonly) NSUInteger length; - 得到字符串中指定下标的字符,返回值是unichar类型的 要打印的话使用%C。
- (unichar)characterAtIndex:(NSUInteger)index; - 判断当前字符串对象和传入的字符串对象的内容是否相同。
- (BOOL)isEqualToString:(NSString *)aString;
注意:不可以使用==
来判断两个OC字符串的内容是否相同,==
运算符的作用: 比较左右两边的数据是否相同,如果两边都是指针变量,那么比较的是指针变量的值也就是地址。 - 将C语言的字符串转换为OC字符串对象。
+ (instancetype)stringWithUTF8String:(NSString *)string; - 将OC字符串对象转换为C语言的字符串.
@property (nullable, readonly) __strong const char *UTF8String - 将字符串写入到指定的文件中。
- (BOOL)writeToFile:(NSString * )path atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError ** )error; - 将文件中的内容读取到字符串中.
+ (nullable instancetype)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error; - 使用NSURL读写资源,字符串提供了对应的方法去读写NSURL对象中封装的资源路径
//从指定资源路径读取文本内容.
+ (nullable instancetype)stringWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error;
//将字符串的内容写入到资源路径中.
- (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error;
// 如果要向网页或者ftp写内容要有权限.
NSString比较
- 字符串比较.
- (NSComparisonResult)compare:(NSString *)string;
比较的原理:比较两个字符串的第 0 个字符的 ASCII 的大小. 如果相等再比较下 1个. 返回值是1个 NSComparisonResut 类型的枚举.
typedef NS_ENUM(NSInteger, NSComparisonResult)
{
NSOrderedAscending = -1L, //第一个字符串小于传入的字符串
NSOrderedSame, // 相等
NSOrderedDescending // 带个字符串大于传入的字符串
};
- 字符串比较:附加条件的比较
- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask;
NSStringCompareOptions参数是一个枚举值举两个常用的
NSCaseInsensitiveSearch: 忽略大小写的比较.
NSLiteralSearch: 完全匹配的比较.
- 判断字符串是否以指定的字符串开头
- (BOOL)hasPrefix:(NSString *)str; - 判断字符串是否以指定的字符串结尾
- (BOOL)hasSuffix:(NSString *)str; - 在主串中搜索子串.从前往后搜索. 第1次匹配的子串.
- (NSRange)rangeOfString:(NSString *)searchString;
返回值是1个NSRange类型的结构体变量.
typedef struct _NSRange {
NSUInteger location; 代表子串在主串出现的下标.
NSUInteger length; 代表子串在主串中匹配的长度.
} NSRange;
如果没有找到:location 为NSUInteger的最大值, 也就是NSNotFound,length的值为0。所以,判断主字符串中是否包含子字符串,只需要判断返回的NSRange结构体变量的length是否为0就可以。
- 在主串中搜索子串.从后往前搜索. 第1次匹配的子串.
NSString *str = @"i love iOS love!";
NSRange range = [str rangeOfString:@"love" options:NSBackwardsSearch];
NSString的截取
- 字符串的截取.截取到字符串中的1部分.
- (NSString *)substringFromIndex:(NSUInteger)from; 从指定的下标出一直截取到最后.
- (NSString *)substringToIndex:(NSUInteger)to; 从第0个开始截取指定的个数.
- (NSString *)substringWithRange:(NSRange)range; 截取指定的1段范围.
- 字符串的替换
- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement
将字符串中第1个参数替换为第2个参数.原来的指针指向字符串的内容是不会变的,新串是以方法的返回值返回的.如果串1中有多个相同的被替换的串,会全部替换。
这个方法还可以做删除串中的一些字符. 原理: 将想要删除的字符替换为@""即可。
- 字符串数据转换为其他的类型.
@property (readonly) double doubleValue;
@property (readonly) float floatValue;
@property (readonly) int intValue;
@property (readonly) NSInteger integerValue
@property (readonly) long long longLongValue
@property (readonly) BOOL boolValue
转换注意. 从头开始转换,能转换多少就是多少. 到遇到不能转换的时候就停止转换.
- 去掉字符串前后的空格,中间的空格无法去掉。
str = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
- 将转换字符串大小写
@property (readonly, copy) NSString *uppercaseString; // 转换为大写
@property (readonly, copy) NSString *lowercaseString; // 换换为小写
- 去掉字符串前后的大写或小写字母,或掉头尾指定的字符串
- (NSString *)stringByTrimmingCharactersInSet:(NSCharacterSet *)set;
[NSCharacterSet characterSetWithCharactersInString:@""]
NSMutableString
NSMutableString为可变的字符串,与NSString不同的是,无论NSMutableString修改多少次,对象始终只有一个,每次修改字符串的时候,修改这个对象中的内容即可,不会再重新创建对象。NSMutableString继承自NSString,并对NSString进行了扩展,同样用来存储字符串。
NSMutableString的初始化
NSMutableString *str1 = [[NSMutableString alloc]init];
NSMutableString *str2 = [NSMutableString string];
注意:不能直接初始化一个字符串常量给NSMutableString
例如:NSMutableString *str = @"xx_cc";
因为@"xx_cc"是一个父类对象,而str指针是一个子类指针,子类指针不能指向父类对象,当调用str的子类独有方法的时候就会出错。
NSMutableString的常用方法
- 追加新的字符串
- (void)appendString:(NSString *)aString;
- (void)appendFormat:(NSString *)format, ... ;
- NSMutableString转化为NSString,此时转化后的字符串就具备了恒定型
+ (instancetype)stringWithString:(NSString *)string;
同时NSMutableString也具备NSString的所有方法。
NSString与NSMutableString的使用场景
当我们需要多次使用一个固定的字符串的时候还是尽量使用NSString,因为NSString的恒定性,保证不会创建多余的重复的对象,效率会更高,而当需要大量拼接字符串的时候,需要使用NSMutablString,保证每次拼接都是操作一个对象,不会重复创建多个对象,效率较高。
NSArray
NSArray的特点
- NSArry只能存储OC对象,不能存储非OC对象,所以基本数据类型等非OC对象需要转化为OC对象才可以存储。
- 这些对象在NSArray数组中有序存放一个挨着一个。
- NSArray是不可变得,一旦初始化完毕之后,它里面的元素就永远是固定的,无法删除和新增元素
NSArray的创建
+ (instancetype)array; // 创建一个没有任何元素的数组
+ (instancetype)arrayWithObject:(ObjectType)anObject; // 创建只有一个OC对象的数组
+ (instancetype)arrayWithObjects:(ObjectType)firstObj, ... ; // 使用多个OC对象初始化数组
+ (instancetype)arrayWithArray:(NSArray<ObjectType> *)array;// 使用一个数组初始化另外一个数组
- (instancetype)initWithObjects:(ObjectType)firstObj, ... ;
- (instancetype)initWithArray:(NSArray<ObjectType> *)array;
- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag; // 不知道YESorNO有什么区别
+ (nullable NSArray<ObjectType> *)arrayWithContentsOfFile:(NSString *)path; // 从文件中读取一个数组数据
+ (nullable NSArray<ObjectType> *)arrayWithContentsOfURL:(NSURL *)url; // 从URL路径中读取一个数组数据
- (nullable NSArray<ObjectType> *)initWithContentsOfFile:(NSString *)path;
- (nullable NSArray<ObjectType> *)initWithContentsOfURL:(NSURL *)url;
需要注意的是NSArray中以nil作为标志表示数组元素传递结束,当设置一个对象为nil,NSArray就会以此为标志结束存储,后面的对象就没有办法存在数组中了。
因此NSArray中不能存储nil,会将nil作为元素结束的标志,并且NSArray中只能存储OC对象,而nil等于0为基本数据类型。
NSArray的常用方法
@property (readonly) NSUInteger count; // 获取数组中元素的个数
- (ObjectType)objectAtIndex:(NSUInteger)index; // 获取数组中指定下标的元素的值
- (BOOL)containsObject:(ObjectType)anObject; // 判断数组中是否包含指定元素
- (BOOL)isEqualToArray:(NSArray<ObjectType> *)otherArray; // 判断两个数组是否相同
@property (nullable, nonatomic, readonly) ObjectType firstObject; // 获取数组中第一个元素
@property (nullable, nonatomic, readonly) ObjectType lastObject; // 获取数组中最后一个元素
- (void)makeObjectsPerformSelector:(SEL)aSelector ; // 使数组中所有元素发送消息
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(nullable id)argument // 是数组中所有元素发送带参数的消息
- (NSUInteger)indexOfObject:(ObjectType)anObject; // 查找指定元素第一次出现的下标
- (NSUInteger)indexOfObject:(ObjectType)anObject inRange:(NSRange)range; // 在一定范围内查找指定元素
NSArry的遍历
使用for循环、for in循环均可以遍历数组,for in 循环直接就可以遍历出数组元素的值,要求参数变量必须与数组中元素类型一致,如果数组中元素类型不一致,则可以使用id万能指针。
另外NSArray也提供了在block中遍历数组元素的方法
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// obj:代表便利出来的元素的值 。 idx:代表元素的下标
// 如果希望停止遍历,将stop指针指向的变量赋值为NO即可
}];
NSArray与字符串
NSArry和NSString中分别提供了将数组元素拼接成字符串和将字符串分割成数组的方法
数组提供的拼接字符串方法
// 将数组中的所有元素用separator拼接成一个字符串。
- (NSString *)componentsJoinedByString:(NSString *)separator;
字符串提供的分割字符串组成数组的方法
将字符串以separator分割,每一个字符串成为数组元素存入到数组中
- (NSArray<NSString *> *)componentsSeparatedByString:(NSString *)separator;
NSMutableArray
NSMutableArray是NSArray的子类,所以NSMutableArray也可以用来存储数据,唯一不同的是NSMutableArray是可变数组,存储在该数组中的元素可以删除,也可以动态增加元素,其他用法均与NSArray相同。
NSMutableArray常用方法
- (void)addObject:(ObjectType)anObject; // 添加元素,默认添加在最后
- (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index; // 往指定位置添加元素
- (void)removeLastObject; // 删除最后一个元素
- (void)removeObjectAtIndex:(NSUInteger)index; // 删除指定元素
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(ObjectType)anObject; // 替换指定位置元素
- (void)addObjectsFromArray:(NSArray<ObjectType> *)otherArray; // 添加另一个数组内的所有元素
- (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2; // 交换两个位置的元素
- (void)removeAllObjects; // 删除所有元素
- (void)removeObject:(ObjectType)anObject inRange:(NSRange)range; // 删除指定范围内的指定元素
- (void)removeObject:(ObjectType)anObject; // 删除指定元素
- (void)removeObjectIdenticalTo:(ObjectType)anObject inRange:(NSRange)range; // 在指定范围删除与之相同的元素
- (void)removeObjectIdenticalTo:(ObjectType)anObject; // 删除与之相同的元素
- (void)removeObjectsInArray:(NSArray<ObjectType> *)otherArray; // 删除与另一数组中元素相同的元素
- (void)removeObjectsInRange:(NSRange)range; // 删除指定范围内的元素
- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray<ObjectType> *)otherArray range:(NSRange)otherRange; // 使用另外一组元素的指定范围替换数组内指定范围元素
- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray<ObjectType> *)otherArray; // 使用另外一个数组替换数组内指定范围元素
注意:NSMutableArray不能使用语法糖@[]创建,因为语法糖创建的是不可变数组。
NSArray的数据持久化
有时我们需要将数组的信息(数组的元素的值)保存在沙盒中,进行数据持久化,当使用到的时候在重沙盒中读取
可以使用plist文件保存数组,即将数组的信息存储到plist文件中,就会将数组的所有的元素存储到这个文件中。
NSArray提供了写入数据和读取数据的方法。
- 将数组写入plist文件中
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
- 将plist文件中的数据还原为一个数组。
+ (nullable NSArray<ObjectType> *)arrayWithContentsOfFile:(NSString *)path;
NSDictionary
NSDictionary以键值对的形式存储数据,唯一的key对应value,通过key来找到存储在字典中的value。NSDictionary字典一旦创建完毕,其键值对的个数就已经固定,无法删除,新增。
NSDictionart的创建
// key = name value = xx_cc ,前面的是value后面的是key
NSDictionary *dict =[NSDictionary dictionaryWithObjectsAndKeys:@"xx_cc",@"name",@"18",@"age", nil];
// 也可以通过快速创建 key:value
NSDictionary *dict2 = @{@"name":@"xx_cc",@"age":@"18"};
NSDiction的常用方法
@property (readonly) NSUInteger count; // 获取字典中键值对的个数
- (nullable ObjectType)objectForKey:(KeyType)aKey; // 获取键对应的值
@property (readonly, copy) NSArray<KeyType> *allKeys; // 获取所有的key
@property (readonly, copy) NSArray<ObjectType> *allValues; // 获取所有value
NSDictionary的遍历
除使用for in遍历之外,同样可以使用block进行遍历
[dict2 enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// key 键 ,obj 值
}];
NSMutableDictionary
可变字典,相对于字典,可以进行键值对的删除和新增
- (void)removeObjectForKey:(KeyType)aKey; //删除key对应的键值对
- (void)setObject:(ObjectType)anObject forKey:(KeyType <NSCopying>)aKey; // 添加键值对
- (void)removeAllObjects; // 删除所有的键值对
- (void)removeObjectsForKeys:(NSArray<KeyType> *)keyArray; // 删除数组中所有key的键值对
NSDictionary数据持久化
NSDictionary也可以将字典数组的信息持久化起来。
- 将字典数组的信息保存到plist文件中.
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
- 从plist文件中还原回字典.
+ (nullable NSDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfFile:(NSString *)path;
NSDictionary字典数组存储数据的原理.
当往字典数组中存储1个键值对的时候,这个键值对并不是按照顺序挨个挨个的存储的,存储键值对的时候,会根据键和数组的长度做1个哈希算法,算出1个下标,将这个键值对存储在该下标处。
取值的时候:也是根据键做1个哈希算法,就可以算出这个键值对存储的下标,然后直接找到这个下标的数据取出就可以了。
NSDictionary与NSArray对比
存储过程
NSArray数组的元素按照顺序存储,
NSDictionary不是按照顺序存储的,存储的下标是通过哈希算法算出来的。存取的效率
存储的时候NSArray效率要高一些,因为NSArray不需要计算下标,直接往后存储即可。
取得时候: 如果取值的时候,是将所有元素全部取出来,NSArray效率更高一些
如果取值的时候,只会取数组中指定的几个元素,字典数组取值更快一些。
NSNumber
因为NSArray和NSDictionary都无法存储基本数据类型,所以NSNumber就是用来将基本数据类型转化为对象的。
基本数据类型存入数组需要先将基本数据类型转化为对象,然后存入数组或者字典中。
基本数据类型转化为NSNumber对象
+ (NSNumber *)numberWithChar:(char)value;
+ (NSNumber *)numberWithUnsignedChar:(unsigned char)value;
+ (NSNumber *)numberWithShort:(short)value;
+ (NSNumber *)numberWithUnsignedShort:(unsigned short)value;
+ (NSNumber *)numberWithInt:(int)value;
+ (NSNumber *)numberWithUnsignedInt:(unsigned int)value;
+ (NSNumber *)numberWithLong:(long)value;
+ (NSNumber *)numberWithUnsignedLong:(unsigned long)value;
+ (NSNumber *)numberWithLongLong:(long long)value;
+ (NSNumber *)numberWithUnsignedLongLong:(unsigned long long)value;
+ (NSNumber *)numberWithFloat:(float)value;
+ (NSNumber *)numberWithDouble:(double)value;
+ (NSNumber *)numberWithBool:(BOOL)value;
+ (NSNumber *)numberWithInteger:(NSInteger)value;
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
也可以使用@直接向基本数据类型封装为对象
@10 代表是1个NSNumber对象,这个对象中包装的是整形的10。如果后面的数据是1个变量,那么这个变量就必须要使用小括弧括起来。
NSNumber对象转化为基本数据类型
@property (readonly) char charValue;
@property (readonly) unsigned char unsignedCharValue;
@property (readonly) short shortValue;
@property (readonly) unsigned short unsignedShortValue;
@property (readonly) int intValue;
@property (readonly) unsigned int unsignedIntValue;
@property (readonly) long longValue;
@property (readonly) unsigned long unsignedLongValue;
@property (readonly) long long longLongValue;
@property (readonly) unsigned long long unsignedLongLongValue;
@property (readonly) float floatValue;
@property (readonly) double doubleValue;
@property (readonly) BOOL boolValue;
@property (readonly) NSInteger integerValue;
@property (readonly) NSUInteger unsignedIntegerValue;
NSValue
我们常常遇到NSRange、CGPoint、CGSize、CGRect这些结构体,他们的变量没有办法存储到集合之中,因此需要先将这些结构体变量存储到OC对象中,再将OC对象存储到集合之中,NSValue类的对象就是用来包装结构体变量的。
NSValue的使用也非常简单,这里不在赘述了。
NSDate
日期类也是会经常使用到的,通常需要将服务器返回的时间进行一些处理,或者与当前时间进行计算,然后显示。
NSDate 时间处理
获得当前时间,得到的是当前系统的格林威治时间,0时区的时间。
NSDate *date = [NSDate date];
NSLog(@"%@",date);
格式化输出日期
系统默认的格式 年-月-日 时:分:秒 +时区。
如果想要将时间按照我们既定的格式输出
- 先要创建1个NSDateFormatter对象,这个对象作用就是将1个日期转换成1个指定的格式.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; - 告诉这个日期格式化器对象,要把日期转换个什么样子的。
yyyy: 年份.
MM: 月份.
mm: 分钟.
dd: 天.
hh: 12小时.
HH: 24小时
ss: 秒
formatter.dateFormat = @"yyyy年MM月dd日 HH点mm分ss秒";
- 使用日期格式化器 将指定的日期转换指定格式的字符串.
NSString *str =[formatter stringFromDate:date];
NSLog(@"str = %@",str);
- NSDate提供了日期类型与字符串相互转换的方法
- (NSString *)stringFromDate:(NSDate *)date; //将日期类型换换为字符串
- (NSDate *)dateFromString:(NSString *)string;//将字符串转换为日期对象.
注意: NSDate取到的时间是格林威治的时间,而NSDateFormatter转换成字符串以后,会自动转换为当前系统的时区的时间。
NSDate计算时间的方法
- 在当前时间的基础之上,新增指定的时间,得到的1个新的时间, 传入1个负数 就是在当前时间的基础之上减指定的秒数。
+ (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
NSDate *d1 =[NSDate dateWithTimeIntervalSinceNow:8*60*60];
- 求两个时间之间的差,可以用来计算出执行代码所花费的时间。
- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate;
- 得到NSDate中的年月日时分秒。
得到时间的各个部分,可以使用日期格式化器NSCalendar来得到,直接来看一个例子吧
NSDate *date = [NSDate date];
//1.创建1个日历对象. 调用类方法currentCalendar得到1个日历对象.
NSCalendar *calendar = [NSCalendar currentCalendar];
//2.指定日历对象取到日期的对象的那些部分. 是要取那1个时间对象的部分.
// 返回1个日期组件对象.这个对象中就有指定日期的指定部分.
NSDateComponents *com = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:date];
NSLog(@"%ld-%ld-%ld",com.year,com.month,com.day);
文中只是将常用的方法简单列举了一下,这些方法的功能需要做到心中有数,用到的时候去API里面查询即可。也有一些方法虽然不常用,但是用到的时候,这些方法可以很方便的帮我们解决问题,不需要自己去实现走很多弯路。
文中如果有不对的地方欢迎指出。我是xx_cc,一只长大很久但还没有二够的家伙。