Foundation框架 | 青训营笔记

51 阅读10分钟

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去掉用的情况:

  1. 在运行时动态通过Runtime添加/替换进来的方法,在编译时不存在
  1. (不推荐) 在明确知道该对象有些实现私有方法,但没有声明在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中处理字符串的类

    1. 严格来说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. 创建

      • 若是双引号前面不加 @符号,那就是纯的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"];
    
    1. 类型转换

      • 如果要将其他类型单独或组合转换成字符串,可以使用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];
    
    1. 子字符串——一些方法
    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子类

      1. NSArray它也不是纯值类型,所以具备了许多实现好的数组访问方法。(比C语言数组方便)
      1. 跟NSString一样,创建NSArray也有简写,使用 @ + 方框 就相当于创建一个NSArray对象

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

      1. 如果想保存基本类型的话可以通过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查询

      1. 数组的查询都是针对对象指针的,即另外一个虽然是同样内容的字符串查询,但因为不是同个对象,也会有查询不到的情况。
      1. 如果你想要寻找同等值的,可以使用 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

    image-20220726233138885

    想对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 字典

      1. 基于Key-Value键值对访问的数据类型,使用 @花括号就可以快捷的创建字典对象,一般就使用NSString当做字典的Key做访问,而Value呢跟数组一样必须是个Objective-C的对象,如果要保存基础类型一样可以先转成NSNumber。
      1. 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对象

崩溃:

image-20220726234156741

正确:

\