Foundation框架
这是我参与「第四届青训营 」笔记创作活动的的第4天。
今天整理了上课的系统Foundation框架部分,比较基础,理解了为什么Objective-c是c#为背景基础的。
Foundation框架为 App和其他框架提供了基础的能力 并且Foundation框架的能力MacOS, iOS, watchOS, tvOS通用
数据存储持久化/文本处理/日期时间计算/排序筛选/网络
- 引入Foundation框架的Foundation.h头文件
import <Foundation/Foundation.h>
Foundation头文件里的内容就是把Foundatino框架中公开类的头文件引入,一行引入就等于把所有类的声明读引入,Foundation.h这种没有其他声明的,但是把所有公开类都引入的头文件,称为 Umbrella Header 头文件
//main.m
// 引入 Foundation框架的 Foundation.h头文件
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello");
}
return 0;
}
-
NSObject类(Foundation类之一)
- 所有类的根类 (root Class)
- 大量的适应Objective-C语言的方法
- NSObject是一个遵守NSObject协议的类
NSObject头文件里 NSOject类遵循了声明的NSObject协议
//NSObject.h
@protocol NSObject
//.....
- (id)performSelector:(SEL)aSelector;
- (BOOL)isKindOfClass:(Class)aClass;
//.....
@end
@interface NSObject <NSObject> {
//.....
+ (instancetype)alloc
- (instancetype)init
//.....
}
-
分配内存空间 & 初始化 创建任何一个对象都会用到2个方法: alloc 和 init
-
调用静态方法 alloc 系统会为这个对象分配内存并返回一个空的实例
-
再调用init实例方法进行初始化
通常子类也会覆盖这个方法去做相应的初始化
-
另外一个用法 new ,它等于 alloc + init,自己定义的类或是其他系统类,也都可以使用 new 去创建对象,代替 alloc + init
-
// NSObject.h
@interface NSObject <NSObject> {
/** 为新对象分配内存空间 */
+ (instancetype)alloc;
/** 初始化对象 */
- (instancetype)init;
/** 为新对象分配内存空间并初始化, 等于[[NSObject alloc] init] */
+ (instancetype)new;
}
//main.m
ByteDancer *byteDancer1 = [[ByteDancer alloc] init];
// 等同于
ByteDancer *byteDancer2 = [ByteDancer new];
- 发送消息(方法调用)
通过 performSelector调用,不会在编译时做任何的校验,(创建一个该对象没有在interface声明的方法选择器并调用),那由于没有编译器校验,如果该方法没实现会崩溃,所以一般在使用时会搭配 respondsToSelector 提前确认该对象是否响应消息
通常这种 interface没有声明还要使用performSelector去掉用的情况:
- 在运行时动态通过Runtime添加/替换进来的方法,在编译时不存在
- (不推荐) 在明确知道该对象有些实现私有方法,但没有声明在interface时,会使用 performSelector去调用
// NSObject.h
@protocol NSObject
/** 发送指定的消息给对象, 返回消息执行结果(相当于方法调用) */
- (id)performSelector:(SEL)aSelector;
/** 发送带一个参数的消息给对象, 返回消息执行结果(相当于方法调用) */
- (id)performSelector:(SEL)aSelector withObject:(id)object;
/** 判断对象是否能够调用给定的方法 */
- (BOOL)respondsToSelector:(SEL)aSelector;
@end
// main.m
// 先判断对象是否能调用方法,再执行调用方法
if ([bytedancer1 respondsToSelector:@selector(sayHello)]) {
// 调用无参无返回值方法
[bytedancer1 performSelector:@selector(sayHello)];
}
// 可以用于调用私有方法
if ([bytedancer1 respondsToSelector:@selector(private_method)]) {
[bytedancer1 performSelector:@selector(private_method)];
}
- 类关系判断 判断对象之间类的关系,以及获取对象的类的方法
注意一个特别的类型 Class,这种参数它不是对象,就是一个类的型别。
使用场景:判断类别常用在不确定某个对象的类型时,需要先使用if判断加上 isKindOfClass方法,确定是某个类型后,在去做类型转换根方法调用
// NSObject.h
/** 获取当前对象的类 */
- (Class)class;
/** 获取当前对象的类 */
- (Class)superclass;
/** 判断对象是否是给定类或给定类子类的实例 */
- (BOOL)isKindOfClass:(Class)aClass;
/** 判断对象是否是给定类的实例 */
- (BOOL)isMemberOfClass:(Class)aClass;
/** 判断对象是否遵从给定的协议 */
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
例如你从一个数组或字典里取出来一个对象,你只能确定这个对象保存的可能是字符串,数字,或是某个字定义类。
// 字典对应的不确定类型
NSObject *getValue = [dictionary1 objectForKey:@"key1"];
if ([getValue isKindOfClass:NSString.class]) {
// 确定是NSString 在转型成NSString
NSString *strValue = (NSString *)getValue;
[strValue length];
} else if([getValue isKindOfClass:ByteDancer.class]) {
// 确定是ByteDancer 在转型成ByteDancer
ByteDancer *byteDacner1 = (ByteDancer *)getValue;
[byteDacner1 sayHello];
} else {
NSLog(@"unkown class");
}
-
数据类型
-
NSString OC中处理字符串的类
-
严格来说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-
创建
- 若是双引号前面不加 @符号,那就是纯的C字符串
- 如果使用了 @双引号,那实际上已经创建了一个NSString对象了
- 也可以通过NSObject的 alloc init方法,像其他类一样创建字符串
// @"" 创建 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"];-
类型转换
- 如果要将其他类型单独或组合转换成字符串,可以使用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];- 子字符串——一些方法
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 数组 NSObject子类
- NSArray它也不是纯值类型,所以具备了许多实现好的数组访问方法。(比C语言数组方便)
-
跟NSString一样,创建NSArray也有简写,使用 @ + 方框 就相当于创建一个NSArray对象
NSArray中不能存放 int, float, bool 等基本类型,只能保存Objective-C对象,但是一个数组对象里面可以保存不同Class的对象,因为每个Objc的对象实际是一个指针,所以从指针的这个角度来看的话,数组存放的每一项不管是什么类的对象都是等长的。这样处理比如length,get、add等操作的时候就非常简单了。
- 如果想保存基本类型的话可以通过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)];-
NSArray查询
- 数组的查询都是针对对象指针的,即另外一个虽然是同样内容的字符串查询,但因为不是同个对象,也会有查询不到的情况。
- 如果你想要寻找同等值的,可以使用 for循环去遍历数组,并比较字符串的内容
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); }-
不可变数组NSArray/可变数组NSMutableArray
- NSArray不能添加或删除树组里面的元素,也就是一旦这个NSArray对象一旦被创建了就没法修改
- NSArray的子类 NSMutableArray,只有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// 不可变 NSArray *arr2 = ..... doSomething(NSArray* array); // arr2 还是原数据// 可变 NSMubaleArray *arr1 = ..... doSomething(NSMubaleArray* array); // arr1 是否已经被修改- 不可变(只读),约束限制,可以减少对象传递上的不确定性,实际上 NSArray的底层实现与NSMutableArray的底层实现区别非常大,NSArray的查找性能确实优于NSMutableArray
想对NSArray增删可以先转为NSMutableArray,再转回NSArray
// NSArray -> NSMutableArray NSMutableArray *mutableArr1 = [[NSMutableArray alloc] initWithArray:numberArrayObject]; // NSMutableArray -> NSArray NSArray *fixArr1 = [[NSArray alloc] initWithArray:mutableArr1];-
NSMutableArray数据增删
如果在声明数组时没有限制数组的存放类型的话,那么任何对象都能被新增到数组里可以单独新增对象,可以指定添加的位置,还能和入另外一个Array,删除则可以使用 删除末尾, 删除指定位置,和移除全部...等
//MARK: 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), }-
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]); }];-
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]; -
总结
@[] = 创建NSArray对象
@“” = 创建NSString对象
@{} = 创建NSDictionary对象
@{} = 创建NSNumber对象
崩溃:
正确:
\