iOS开发之泛型使用

1. 泛型:限制类型

  • 1.1.泛型使用场景:

    • 1.在集合(数组NSArray、字典NSDictionary、集合NSSet)中使用泛型比较常见。

    • 2.当声明一个类,但是类里面的某些属性的类型不确定的时候,我们才使用泛型。

  • 1.2.泛型书写规范

    在类型后面定义泛型:NSMutableArray<UITouch *> dataArray 

  • 1.3.泛型修饰

    只能修饰方法的调用。 

  • 1.4.泛型好处: 

    1.提高开发的规范,减少程序员之间的交流。

    2.通过集合取出来的对象,可以直接当做泛型对象使用。这样我们就可以直接使用.点语法。

2.代码使用泛型:

  • 2.1.声明一个泛型为NSString的数组
    具体做法就是在 NSMutableArray 后带一个 <NSString *> ,尖括号内部即为泛型类型

    @property (nonatomic, strong, nullable) NSMutableArray<NSString *> *dataArray;
  • 2.2.当我们要给数组添加对象或取出对象的时候,系统就会自动提示应该传入或者取出来的对象的类型,这个类型就是你刚才声明的泛型类型
    • 没有使用泛型
      [self.dataArray addObject:<#(nonnull id)#>];
    • 使用泛型
       [self.dataArray addObject:<#(nonnull NSString *)#>];
    • 添加不正确的类型,会出现警告

       [self.dataArray addObject:@1];
    • 我们可以直接将集合中取出来的对象当做泛型使用

      NSInteger length = [self.dataArray.firstObject length];
    • 代码如下
       

3.泛型的自定义

  刚才我们只是实现了系统类NSMutableArray的泛型。接下来我们要考虑下,我们怎么样在我们自己的类中声明一个泛型的属性呢?

  为了这个目的,我们创建一个 Language 的类表示 “语言”。并且创建两个 Language 的子类,分别叫 Java 和 IOS 。很明显这两个是“某一个类型的语言”。我们创建一个Person的类,给类声明一个泛型,在类的 .h 文件中声明一个声明一个属性,这个属性表示这个人会的语言,即为 IOS 或者 Java 。那么我们有以下两种声明方式:  

  • id:任何对象都能传进来
    @property (nonatomic, strong, null_unspecified) id language;
  •  Language:在外面调用的时候不能提示具体是哪种语言
    @property (nonatomic, strong, null_unspecified) Language *language;
  • 代码示例:
    #import "Language.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface Person : Language
    /**! 第一种方式*/
    @property (nonatomic,strong,null_unspecified) id languageOne;
    /**! 第二种方式*/
    @property (nonatomic,strong,null_unspecified) Language *languageTwo;
    @end
    
    NS_ASSUME_NONNULL_END

  因为 Language 这个语言并不能代表这个 Person 究竟会什么语言,我们需要的属性时 IOS 和 Java。这两种都可以在调用的时候传入 Java 和 IOS 两种对象,但它们的缺点也非常明显,若使用 id 则我们可以传入任何对象,而不只是 Java 和 IOS ;使用 Language * 呢,我们没有办法在编译的时候确定这个人究竟会什么语言,而只能在运行时判断。有没有办法让我们在编译的时候就能知道 Person 具体会哪种 Language 呢?

  办法就是泛型。

  声明自定义类的泛型,我们需要做这样一步:

  • (给类)声明一个泛型
    @interface JTPerson<ObjectType> : NSObject

    看出区别了吗?我们在 interface 类名之后加了一对尖括号 <> ,中间是 ObjectType 。这个就代表泛型。这样我们在声明属性的时候就可以这么写: 

    @property (nonatomic, strong, null_unspecified) ObjectType language;

    也就是,我们现在不指定具体的类型,而在实例化这个类的时候确定这个泛型。若不确定,那么所有的 ObjectType 会自动变成 id 。
    像这样:

     // iOS
     JTPerson<IOS *> *iOSP = [[JTPerson alloc] init];
     // [iOSP setLanguage:@"123"];
     // [iOSP setLanguage:<#(IOS * _Null_unspecified)#>];
     // [iOSP setLanguage:[[Java alloc] init]];
     // Java
     JTPerson<Java *> *javaP = [[JTPerson alloc] init];
     // [javaP setLanguage:@"123"];
     // [javaP setLanguage:<#(Java * _Null_unspecified)#>];
     // [javaP setLanguage:[[IOS alloc] init]];

    这样,我们在声明 Person这个类的时候,就顺便声明了这个类的泛型。这样系统就会在你使用到泛型的属性与方法的时候,自动提醒你声明的泛型类型了。

     

4. 泛型的协变与逆变 

  下图是系统 NSArray 的头文件部分,可以看到它使用了自定义泛型并命名为 OBjectType,

  在自定义泛型前加了一个 __covariant 的修饰符,这个修饰符就表示协变性


 

  • __covariant - 协变性,子类型可以强转到父类型(里氏替换原则)
  • __contravariant - 逆变性,父类型可以强转到子类型

posted on 2020-03-17 17:29  梁飞宇  阅读(337)  评论(0编辑  收藏

统计