前言
不久前,公司决定在一个 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 关键字,并使用空格将它们俩分开: