黑马程序员——ObjC基础—其他封装

----------- Java 培训Android 培训IOS 培训.Net 培训、期待与您交流!------------ 

本节来学习一下OC中对基本数据类型以及代码块的封装。

1、Block封装
在OC中,采用Block代码块来对c语言函数进行封装,以扩充c函数的适用范围,我们用c语言定义一个sum函数:
int sum(int a, int b){
    return a + b;
}
这是一个标准的c函数,包含了输入参数和返回类型,我们看看Block封装后的样子:
int (^sumBlk)(int,int)=^(int a, int b){
    return  a + b;
}; // 留意这里的分号
在这里我们创建了一个名为sumBlk的代码块,他也能接收两个参数,并具有返回类型,无参数的代码块怎么定义呢?像下面这样:
void (^helloWorldBlk)()=^{ // 这里的小括号不能省略
    NSLog(@"Hello World!");
};
对于Block代码块,采用和c函数相似的调用方式:
    int a = sumBlk(10,20); // 含参Block
    helloWorldBlk(); // 无参Block
我们这么大废周折的把函数封装起来,这样有什么意义呢?
a、首先要了解的是,Block代码块本身是一个OC对象,遵循OC的垃圾回收机制,有利于存储空间的分配和管理;
b、c函数使用上一般是和主函数并列的,而Block代码块可以直接在主函数或者其他函数中使用的,可以访问函数体内代码块之外的变量,如下所示:
int main()
{
    int a=1;
    __block int b=1;
    
    void (^getBlk)() = ^{
        int c = a; // 可以访问外部变量
        int d = b;
        
        // a = 2; // 报错,不可修改外部变量
        b = 2; // 外部__block类型可以修改
    };
    
    return 0;
}
在Block内部不能修改外部的变量(__block修饰的除外),只可以访问,在代码块内部实际上是copy了一个const类型的变量,这保证了代码块的独立性,是面向对象的一大特点。
c、Block代码块可以作为另一个代码块或者方法、函数等的参数使用,如下所示:
    // 定义blk_type类型Block
    typedef void (^blk_type)();
    
    blk_type blk = ^(blk_type blk){
        NSLog(@"This is a block!");
    };
    
    // 定义含有blk1_type类型参数的Block
    void (^blk1)(blk_type)=^(blk_type blk){
        // 共用代码
        blk();
        // 公用代码
    };
    
    // 通过blk2调用blk1类型Block
    blk1(blk);
    
    // 通过blk2调用一段代码
    blk1(^{
        // 自定义代码
    });
在代码编写过程中,我们会遇到一类代码,他们开头和结尾相同,中间的部分不同,我们通过上面的方式进行代码块的嵌套,可以实现将公共方法提取出来,形成一个单独的模块,然后传入自定义代码块的以实现不同的功能。如Foundation框架中的设置动画的代码:
    // 公共代码
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:speed];
    
    // 动作的具体内容
    
    // 公共代码
    [UIView commitAnimations];
我们就可以将公共部分提取出来,形成一个方法,再根据具体的情况进行调用:
- (void)actWithBlock:(void (^)())block atSpeed:(NSTimeInterval)speed{
    
    // 公共代码
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:speed];
    
    // 传入代码块
    block();
    
    // 公共代码
    [UIView commitAnimations];
}
这样以来,我们创建类似的代码片段的时候,只需调用该方法即可:
    [MyViewController actWithBlock:^{
    // 自定义代码
    } atSpeed:kRotateSpeed];

2、NSArray和NSMutableArray
在c语言中我们采用数组来保存类型相同的多个对象,多个对象也可以通过数组的方式来管理,如下:
    NSObject *obj[3];
    obj[0] = [NSObject new]; // 元素可以是不同类型
    obj[1] = [NSObject new]; // 元素可以是不同类型
    obj[2] = [NSObject new]; // 元素可以是不同类型
我们也可以采用OC数组的方式来管理(元素也可以不同):
    // 方式① 注意最后需要一个nil参数
    NSArray *arr1 = [[NSArray alloc] initWithObjects:obj1, obj2, obj3, nil];
    
    // 方式② 简化方式
    NSArray *arr2 = @[obj1, obj2, obj3];
NSArray已经创建之后是不能修改的,他可以采用以下的方式进行读取:
    // 方式① 方法
    [arr1 objectAtIndex:2];
    // 方式② 简化
    arr1[2];
NSArray本身是一个对象,遵循对象的的内存管理,并具有一些对象方法,可以方便我们对对象的操作。例如:
    // 比较两个对象是否相同
    [arr1 isEqualTo:arr2];
    // 获取内部数据的index值
    [arr1 indexOfObject:obj2];
    // 是否包含对象
    [arr1 containsObject:obj3];
方法还有很多,在这里不一一介绍。
NSMutableArray类是NSArray的一个子类,他在NSArray的基础上,扩展了内部成员可变的特性:
    NSMutableArray *marr1 = [[NSMutableArray alloc] init];
    
    // 添加对象
    [marr1 addObject:obj1];   // obj1
    // 通过NSArray添加
    [marr1 addObjectsFromArray:arr1]; // obj1  obj1  obj2  obj3
    // 插入对象
    [marr1 insertObject:obj3 atIndex:1]; // obj1  obj3  obj1  obj2  obj3
    // 交换
    [marr1 exchangeObjectAtIndex:1 withObjectAtIndex:3]; // obj1  obj2  obj1  obj3  obj3
    // 删去
    [marr1 removeObject:obj1]; // obj2 obj3 obj3
NSMutableArray不能使用@[]的方式进行简化,但是可以使用[index]的方式来访问,NSMutableArray和NSArray之间可以相互转化:
    // 通过NSArray创建
    NSMutableArray *marr2 = [NSMutableArray arrayWithArray:arr1];
    // copy自NSMutableArray
    NSArray *arr3 = [marr2 copy];
很明显,采用NSMutableArray有其可以更改的优势,但是相应的要占有更多的资源,在一些不需要改动的场合,采用NSArray更为合适,有一点需要注意的是,这两个方法都可以copy自己,NSMutableArray复制出的是一个NSArray,而NSArray只是复制出一个指针,因为他本身不能变化,也就没必要复制出新的对象。
我们可以采用下面的方式来遍历OC数组的每一个元素:
    for (id obj in arr1) {
        NSLog(@"%@", obj);
    }
这个方式的优点是不需要知道OC数组内部有多少数据。

3、NSSet和NSMutableSet
NSSet是一种特殊的OC数组,他的特点是在各个元素的存储不连续,没有相对关系,采用hash方式(散列算法)来压缩存储元素,保证一个Set里不存在两个相同的对象,同时,该方式另一个好处是内部元素的检索速度很快。
    // NSSet的创建
    NSSet *set1 =[NSSet setWithObjects:obj1, obj2, obj3, nil];
    NSSet *set2 =[NSSet setWithArray:arr1];
    NSSet *set3 = [NSSet setWithSet:set1];
NSSet对象内部的元素不能直接引索,他有以下几种使用方法:
    // 提取所有元素(返回一个NSArray)
    [set1 allObjects];
    // 任意取一个
    [set1 anyObject];
    // 判断是否包含对象
    [set1 containsObject:obj1];
    // 判断是否是另一个Set的子集
    [set1 isSubsetOfSet:set2];
    // 判断是否包含另一个Set
    [set1 intersectsSet:set2];
NSMutableSet是NSSet的子类,他也具有可以任意修改的特点,创建和使用如下:
    // 创建可变set
    NSMutableSet *mset1 = [NSMutableSet setWithArray:arr1];  // obj1 obj2 obj3
    NSSet *set4 = [NSSet setWithObject:obj2];

    // 两个set合并
    [mset1 unionSet:set4]; // obj1 obj2 obj3 (不变)
    // 两个set相减
    [mset1 minusSet:set4]; // obj1 obj3
    // 求交集
    [mset1 intersectsSet:set4]; // obj2
    // 删除元素
    [mset1 removeObject:obj2]; // 空
    [mset1 removeAllObjects]; // 全部删除
Set数据也可以进行元素的遍历:
    for (id obj in mset1) {
        NSLog(@"%@", obj);
    }

4、NSDictionary和NSMutableDictionary
OC字典是对c字典的封装,我们直接看代码:
    // 方式① 创建
    NSDictionary *dic1 = [NSDictionary dictionaryWithObjects:@[obj1, obj2, obj3] forKeys:@[@"o1", @"o2", @"o3"]];
    // 方式② 创建
    NSDictionary *dic2 = @{@"o1": obj1,
                           @"o2": obj2,
                           @"o3": obj3};
字典也可以通过一个外部的文件创建,我们会在后面学习,调用:
    [dic1 objectForKey:@"o1"];
    dic1[@"o1"];
NSMutableDictionary是NSDictionart的子类,他可以动态添加删除字典元素:
    // 通过NSDictionary创建
    NSMutableDictionary *mdic1 = [NSMutableDictionary dictionaryWithDictionary:dic1];
    // 添加,如果key相同则是覆盖
    [mdic1 setObject:obj3 forKey:@"o4"];
OC字典也可以遍历内部元素:
    for (id obj in dic1) {
        NSLog(@"%@", obj);
    }
需要注意的是,OC字典内部的元素是处于无序状态的,他们在存储上,没有先后顺序,这一点和OC数组不同,需要注意。

4、NSString和NSMutableString
NSString在前面已经接触过了,他是OC对c字符串的封装,他也是不可以变化的,创建方式如下:
    // 普通创建
    NSString *str1 = [NSString stringWithFormat:@"I am %d years old!", 10];
    // 简化创建
    NSString *str2 = @"Happy new year!";
NSMutableString也是NSString的子类,他是可以变化的,因此:
    // 创建
    NSMutableString *mstr1 = [NSMutableString stringWithString:str1]; // I am 10 years old now!
    // 尾部添加
    [mstr1 appendString:str2]; // I am 10 years old now!Happy new year!
    // 删除
    NSRange r1 = [mstr1 rangeOfString:@" now"];
    [mstr1 deleteCharactersInRange:r1]; // I am 10 years old!Happy new year!
    // 修改
    NSRange r2={17,1};
    [mstr1 replaceCharactersInRange:r2 withString:@", "]; // I am 10 years old, Happy new year!
    // 更换
    [mstr1 setString:@"a oc string"]; // a oc string
使用OC字符串,相比c字符串还有很多实用的功能,我们在用到的时候再学习。

5、NSValue和NSNumber
NSValue类是OC语言对结构体或其他结构数据的封装,实现方式如下:
    // 定义结构体类型 Student
    typedef struct {
        char *name;
        int age;
    } Student;
    
    // 创建结构体 stu1
    Student stu1 = {"Xiaoming", 18};
    
    // 结构体封装
    NSValue *v1 = [NSValue value:&stu1 withObjCType:@encode(Student)];
    // NSValue *v2 = [NSValue valueWithBytes:&stu1 objCType:@encode(Student)]; // 效果一样
    
    // 创建 stu2 结构体用来接收数据
    Student stu2;
    
    // 将数据取出到stu2
    [v1 getValue:&stu2];
    
    // 访问
    NSLog(@"%s", stu2.name);
    NSLog(@"%d", stu2.age);
NSNumber是NSValue的子类,可以存储普通数据类型,我们看看怎么创建NSNumber对象:
    // 通过c基本数据类型创建数字对象
    NSNumber *n1 = [NSNumber numberWithBool:YES];
    NSNumber *n2 = [NSNumber numberWithChar:'A'];
    NSNumber *n3 = [NSNumber numberWithFloat:1.2345f];
    NSNumber *n4 = [NSNumber numberWithInt:12];
NSNumber对象一经创建,是无法修改的,我们可以通过以下的方式来读取:
    n1.intValue;
    n1.charValue;
    n1.floatValue;
    n1.stringValue;
利用这些方法,可以实现类似类型转换的功能,其中stringValue可以将一个数值转化为字符串对象。
在OC中,我们把c数据类型封装成为对象,就可以按照对象的方式来处理,比如放在NSArray、NSDictionary等中,方便数据的处理。


----------- Java 培训Android 培训IOS 培训.Net 培训、期待与您交流!------------ 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值