当前位置 主页 > 网站技术 > 代码类 >

    因为一个Crash引发对Swift构造器的思考分析

    栏目:代码类 时间:2019-10-20 15:05

    前言

    不久前,公司决定在一个 Objective-C 老工程中,开始使用 Swift 进行混合开发。期间,碰到一个与 Swift 类构造过程相关的 Crash。在解决的过程中,对 Swift 构造过程有了更深刻的理解,特作此记录,期望对刚入坑 Swift 开发的同学能有所帮助。

    Crash 回顾

    先来看一下代码,以下定义了 BaseiewController 和 AViewController 两个类:

    // BaseViewController.h
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface BaseViewController : UIViewController
    
    - (instancetype)initWithParamenterA:(NSInteger)parameterA;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    // BaseViewController.m
    #import "BaseViewController.h"
    
    @interface BaseViewController ()
    
    @property (nonatomic, assign) NSInteger parameterA;
    
    @end
    
    @implementation BaseViewController
    
    - (instancetype)initWithParamenterA:(NSInteger)parameterA {
      self = [super init];
    
      if (self) {
        self.parameterA = parameterA;
      }
      return self;
    }
    
    @end

    以上代码段定义了 Objective-C 类 BaseViewController,并且自定义了构造器 initWithParamenterA。

    // AViewController.swift
    import UIKit
    
    class AViewController: BaseViewController {
      let count: Int
    
      init(count: Int, parameterA: Int) {
        self.count = count
        super.init(paramenterA: parameterA)
      }
    
      // 后面的 “initCoder 从哪儿来” 小节会讲讲这个构造器
      required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
      }
    }

    第二块代码段定义了 Swift 类 AViewController,继承自 BaseViewController,并且自定义了构造器 init(count: Int, parameterA: Int),这个构造器还调用到了父类的 initWithParamenterA 构造器。细心的同学可能发现了,代码中还出现了 init?(coder aDecoder: NSCoder) 构造器,对此,在 initCoder 从哪儿来小节会有详细解释。

    代码就这么多。构建运行工程,前往 AViewController 页面,出乎意料,Crash。控制台输出:

    `Fatal error: Use of unimplemented initializer 'init(nibName:bundle:)' for class 'XXX.AViewController'`

    意思是 AViewController 没有实现 init(nibName:bundle:) 方法,从而导致了 Crash。

    对于刚入坑 Swift 不久的同学可能就会有些懵逼。明明在 Objective-C 的时候这样写根本没有问题啊,怎么到 Swift 这儿就 Crash 了呢?

    Swift 类类型的构造过程回顾

    如果想要了解 Crash 的原因,就需要了解 UIViewController 所属的类类型(class)构造器的相关知识。

    注:本小节大部分内容摘自Swift 官方中文教程。

    指定构造器和便利构造器

    Swift 为类类型提供了两种构造器,分别是指定构造器和便利构造器。

    类倾向于拥有极少的指定构造器,普遍的是一个类只拥有一个指定构造器。每一个类都必须至少拥有一个指定构造器。指定构造器语法如下:

    init(parameters) {
      statements
    }

    便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为部分形参提供默认值。一般只在必要的时候为类提供便利构造器。

    便利构造器也采用相同样式的写法,但需要在 init 关键字之前放置 convenience 关键字,并使用空格将它们俩分开: