以前在用C#开发程序的时侯,只要用到数组,必然离不开泛型。再配合集合的扩展方法和LINQ,对于集合数据的操作真是得心应手,非常爽快。后来我转到iOS开发,学会了Objective语言。发现在Objective-C里,一般最常用的数组容器就是NSArray和字典NSDictionary了。可惜这两者都不支持泛型。数据都是以NSObject的类型添加进来,理论上可以保存保存任何的引用类型,这就需要让开发者来确保所有添加的数据类型的一至性。同样的,当从数组容器里取值时,需要将其转化成对应的Model。有时侯还需要进行类型判断。这样的操作不仅增加了代码的复杂性,也很容易出错。而Swift作为一门后起之秀,必然添加了对泛型的支持。弥补了Objective-C对数组操作的安全性,复杂性等的不足,而本篇文章向大家介绍了如果来定义一个泛型类,通过它来作为一种可以永久保存任意数据类型的数据容器,来实现iOS的数据持久化。
什么是泛型
为了更直观一点,首先让我们来看看不支持泛型的Objective-C语言是怎么利用数据容器的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@interface demo:NSObject//自定义对象 @property (nonatomic,copy) NSString* demoString; @end @implementation demo @end NSMutableArray* arr = [NSMutableArray new]; [arr addObject:[NSNumber numberWithBool:YES]]; //添加Bool类型 [arr addObject:@"111"]; //可以字符串 demo* dm = [demo new]; dm.demoString = @"String"; [arr addObject:dm]; //添加自定义对象 NSLog(@"%@",arr); BOOL a = [arr[0] boolValue]; //需要转成Bool NSString* b = arr[1] ; //直接将id类型赋到NSString类型 demo* dm1 = arr[2]; //直接将id赋值给demo类型 NSLog(@"a:%hhd b:%@.dm:%@",a,b,dm1); //可以正确地打印出来 NSString* dm2 = arr[2]; //也可以将本身是demo类型的赋值到String NSLog(@"%@",dm2); //不会报错.运行时dm2本身还是demo类型 dm2.length; //调用length方法就会出错, 打印结果: 2016-03-31 15:29:28.437 DemoObjc[1108:32945] ( 1, 111, "" ) 2016-03-31 15:29:28.438 DemoObjc[1108:32945] a:1 b:111.dm: 2016-03-31 15:29:28.438 DemoObjc[1108:32945] //虽然在代码阶段是NSString类型,但是运行时是demo类型 2016-03-31 15:29:28.438 DemoObjc[1108:32945] -[demo length]: unrecognized selector sent to instance 0x1001025f0 2016-03-31 15:29:28.439 DemoObjc[1108:32945] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[demo length]: unrecognized selector sent to instance 0x1001025f0' ) |
从上面的示例代码可以很清楚地看到,因为Objective-C不支持泛型,所以编译器没有做任何限制,可以在NSMutableArray里添加任何引用类型的数据,这些数据都以id类型保存。在取值时也一样,默认取出来的数据都是id类型,需要将其转化成原先保存的类型才行。这些操作都是需要开发者来确保正确性。如果不小心写错了,编译器也不会给出任何提示或者警告,只有在运行程序时才能触发错误。所以很容易出现隐藏的Bug,同时类型转换也增加了代码的复杂性。
而泛型的出现刚好完全解决了上面的问题:
1 2 3 4 5 6 7 8 9 10 |
var arrInt = [Int]() //定义了一个数据类型是Int类型的数组,其实就是一个只能添加Int类型数据的NSMutableArray。 arrInt.append(1) //添加1 arrInt.append(2) //添加2 arrInt.append("123") //添加字符串“123” //编译器报错,Cannot convert value of type 'String' to expected argument type 'Int' //不能将String转换成Int print(arrInt[0].dynamicType) //取第一个值 ,打印出类型是Int let str:String = arrInt[1] //将第二个值赋值给String //编译器报错, Cannot convert value of type 'Int' to specified type 'String' //不能将转Int换成String |
从上面的代码很容易看出,对于数据类型是Int的数组,只能添加Int类型的数据,其他的数据类型编译器都会报错。取值也一样,取出来的数直接就是Int类型了,不需要再转型了。
而且比较方便的是Swift的数据容器是是可以直接保存值类型的,不需要像Objective-C一样需要将其转成NSNumber了。同样,你也可以声明一个类型为String类型的数组([String]),
或者自定义类的的泛型数组,编译器都会帮你在编码时告诉你正确的数据类型,防止开发者添加错误的数据类型。
使用泛型的优点
使用泛型的优点有很多:
- 泛型提供了一个强类型的编程模型
- 编译时的类型检查减少了运行时发生数据类型转换异常的几率
- 简化了代码,缓解了代码膨胀。
- 性能得到了提升,不需要在运行时再做类型检查。
- 代码的可读性更好,并且有更好的代码智能提示。
其实在最新的XCode 7.X中,苹果也悄悄地加入了Objective-C语言的弱泛型支持,见下面代码。
1 2 3 |
NSMutableArray* arrString =ode"> NSMutableArray* arrString =般最常用的数组容器就是NSArray和字典NSDictionary了。可惜这两者都不支持泛型。数据都是以NSObject的类型添加进来,理论上可以保存保存任何的引用类型,这就需要让开发者来确保所有添加的数据类型的一至性。同样的,当从数组容器里取值时,需要将其转化成对应的Model。有时侯还需要进行类型判断。这样的操作不仅增加了代码的复杂性,也很容易出错。而Swift作为一门后起之秀,必然添加了对泛型的支持。弥补了Objective-C对数组操作的安全性,复杂性等的不足,而本篇文章向大家介绍了如果来定义一个泛型类,通过它来作为一种可以永久保存任意数据类型的数据容器,来实现iOS的数据持久化。
什么是泛型为了更直观一点,首先让我们来看看不支持泛型的Objective-C语言是怎么利用数据容器的
从上面的示例代码可以很清楚地看到,因为Objective-C不支持泛型,所以编译器没有做任何限制,可以在NSMutableArray里添加任何引用类型的数据,这些数据都以id类型保存。在取值时也一样,默认取出来的数据都是id类型,需要将其转化成原先保存的类型才行。这些操作都是需要开发者来确保正确性。如果不小心写错了,编译器也不会给出任何提示或者警告,只有在运行程序时才能触发错误。所以很容易出现隐藏的Bug,同时类型转换也增加了代码的复杂性。 而泛型的出现刚好完全解决了上面的问题:
从上面的代码很容易看出,对于数据类型是Int的数组,只能添加Int类型的数据,其他的数据类型编译器都会报错。取值也一样,取出来的数直接就是Int类型了,不需要再转型了。 使用泛型的优点使用泛型的优点有很多:
其实在最新的XCode 7.X中,苹果也悄悄地加入了Objective-C语言的弱泛型支持,见下面代码。
|