美文网首页iOS开发实用技术
[iOS][OC] 理解并应用OC的泛型提高代码质量

[iOS][OC] 理解并应用OC的泛型提高代码质量

作者: BudSwift | 来源:发表于2018-11-08 07:25 被阅读83次

理解泛型 Generics

Xcode 7 以后 Objective-C(后称OC) 支持了泛型 Generics 的使用,相对 Swift 而言,OC 的泛型要轻量级得多。OC 的泛型的几点理解:

  • 泛型用于指代一种类型,在 OC 中声明了泛型后至少是 id 类型
  • 泛型往往在容器类中使用,比如 NSArrayNSDictionary
  • 具体的类型,由使用具体的泛型指定。

泛型的应用实践

以一个 NSNumber 数组为例,看一下 NSArray 的泛型声明

@interface NSArray<__covariant ObjectType> : NSObject

使用泛型前,要访问一个数组内部对象的属性,只能通过 getter 方法,不能使用点语法,因为通过 index 取值的返回值是 id 类型,而 id 类型是不确定属性的。

- (void)noGenerics {
    NSArray *numbers = @[@1, @2, @3];
    
    // NSInteger i = numbers[0].integerValue;
    // 编译报错 Property 'integerValue' not found on object of type 'id'


    NSInteger j = [numbers[1] integerValue];
    // 或者
    NSNumber *number = numbers[0];
    j = number.integerValue;
}

使用泛型后,返回值的类型是确定的,因而可以直接访问属性,十分便利。

- (void)userGenerics {
// 泛型 <ObjectType> 这里是 NSNumber *
    NSArray <NSNumber *> *numbers = @[@1, @2, @3];
    NSInteger j = numbers[0].integerValue;
}

在其他 Fundation 的集合中也有这类应用,比如

@interface NSMutableArray<ObjectType> : NSArray<ObjectType>
@interface NSDictionary<__covariant KeyType, __covariant ObjectType> : NSObject
@interface NSMapTable<KeyType, ObjectType> : NSObject
@interface NSSet<__covariant ObjectType> : NSObject

在这些集合对象的相关方法的参数以及返回值中都会用到泛型,有显而易见的好处:

  • 比起 id 类型,在编译期间可以方便地进行访问参数的属性或者方法
  • 在传参数时会得到编辑器的辅助,对于类型不匹配的对象会提出警告。
  • 提高了代码的可读性,阅读代码的人可以清晰地知道内部对象的类型
/// NSArray 带有泛型的方法定义
- (void)addObject:(ObjectType)anObject;
- (ObjectType)objectAtIndex:(NSUInteger)index;

NSMutableArray <NSNumber *> *array = [NSMutableArray array]; 
// 声明为 NSNumber 数组

[array addObject:@"str"]; // 在其中加入 NSString 对象会有编译警告
// incompatible pointer types sending 'NSString *' to 
// parameter of type 'NSNumber * _Nonnull'
// 事实上,Xcode 的自动完成会提示录入 一个 NSNumber 对象
// [array addObject:<#(nonnull NSNumber *)#>]

泛型结合协议的使用

如果声明泛型时同时希望对象响应特定的方法/属性,那么可以在泛型上附加协议,实际应用如下:

@protocol SomeProtocol <NSObject>

@property (nonatomic, copy) NSString *name;

@end

@interface ViewController ()

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSArray <UIViewController <SomeProtocol> *> *array;

// 调用
- (void)useGenericProtocol {
    NSString *name = self.array.firstObject.name;
    NSLog(@"name-> %@", name);
}

@end

自定义泛型类

注意 泛型的使用,只能在 @interface 和 @end 之间,即类声明 拓展、分类中,是不能再实现中使用的。
可以限制泛型的范围,比如:

  • 要求泛型继承某父类 :

**@interface GenericClass <__covariant T: SuperClass *> : NSObject **

  • 要求泛型遵循协议
    **@interface GenericClass <__covariant T: id<SomeProtocol>> : NSObject **
  • 既要继承父类又要遵循协议
    **@interface GenericClass <__covariant T: SuperClass<SomeProtocol> *> : NSObject **

以一个弱引用容器举例 WeakWrapper

@interface WeakWrapper < __covariant T> : NSObject

@property (nonatomic, weak) T content; // 酷酷的泛型代替了 id

- (void)justCallAMethodUserGenerics:(T)someObject;
@end

@implementation WeakWrapper

- (void)justCallAMethodUseGenerics:(T)someObject {
        self.content = someObject;
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    WeakWrapper <UIViewController *> *ww1 = [[WeakWrapper alloc] init];
    ww1.content = self;
    // 直接访问 controller 的 title 属性
    NSLog(@"title-> %@", ww1.content.title);
    
    WeakWrapper <UIView *> *ww2 = [[WeakWrapper alloc] init];
    ww2.content = self.view;
    // 直接访问 view 的 frame 属性
    NSLog(@"frame-> %@", NSStringFromCGRect(ww2.content.frame));
}

嗯,看到这么吊炸天的 T 类型属性声明,就快要高潮了。

参考文章

Swift教程 - 泛型
Objective-C 轻量级泛型
Objective C Generics
2015 Objective-C 新特性

加我微信沟通。


相关文章

网友评论

    本文标题:[iOS][OC] 理解并应用OC的泛型提高代码质量

    本文链接:https://www.haomeiwen.com/subject/vwrixqtx.html