<p><span style="color: #ff0000"><strong>前言</strong></span></p>
<p>不久前,公司决定在一个 Objective-C 老工程中,开始使用 Swift 进行混合开发。期间,碰到一个与 Swift 类构造过程相关的 Crash。在解决的过程中,对 Swift 构造过程有了更深刻的理解,特作此记录,期望对刚入坑 Swift 开发的同学能有所帮助。<br>
</p>
<p><span style="color: #ff0000"><strong>Crash 回顾<br>
</strong></span></p>
<p>先来看一下代码,以下定义了 BaseiewController 和 AViewController 两个类:<br>
</p>
<div class="blockcode">
<pre class="brush:cpp;">
// 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</pre>
</div>
<p>以上代码段定义了 Objective-C 类 BaseViewController,并且自定义了构造器 initWithParamenterA。<br>
</p>
<div class="blockcode">
<pre class="brush:cpp;">
// 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")
}
}</pre>
</div>
<p>第二块代码段定义了 Swift 类 AViewController,继承自 BaseViewController,并且自定义了构造器 init(count: Int, parameterA: Int),这个构造器还调用到了父类的 initWithParamenterA 构造器。细心的同学可能发现了,代码中还出现了 init?(coder aDecoder: NSCoder) 构造器,对此,在 initCoder 从哪儿来小节会有详细解释。<br>
</p>
<p>代码就这么多。构建运行工程,前往 AViewController 页面,出乎意料,Crash。控制台输出:<br>
</p>
<blockquote>
<p>`Fatal error: Use of unimplemented initializer 'init(nibName:bundle:)' for class 'XXX.AViewController'`</p>
</blockquote>
<p>意思是 AViewController 没有实现 init(nibName:bundle:) 方法,从而导致了 Crash。<br>
</p>
<p>对于刚入坑 Swift 不久的同学可能就会有些懵逼。明明在 Objective-C 的时候这样写根本没有问题啊,怎么到 Swift 这儿就 Crash 了呢?<br>
</p>
<p><span style="color: #ff0000"><strong>Swift 类类型的构造过程回顾<br>
</strong></span></p>
<p>如果想要了解 Crash 的原因,就需要了解 UIViewController 所属的类类型(class)构造器的相关知识。</p>
<p>注:本小节大部分内容摘自<a href="https://swiftgg.gitbook.io/swift/swift-jiao-cheng/14_initialization" rel="external nofollow" target="_blank">Swift 官方中文教程</a>。</p>
<p><strong>指定构造器和便利构造器<br>
</strong></p>
<p>Swift 为类类型提供了两种构造器,分别是指定构造器和便利构造器。<br>
</p>
<p>类倾向于拥有极少的指定构造器,普遍的是一个类只拥有一个指定构造器。每一个类都必须至少拥有一个指定构造器。指定构造器语法如下:<br>
</p>
<div class="blockcode">
<pre class="brush:cpp;">
init(parameters) {
statements
}</pre>
</div>
<p>便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为部分形参提供默认值。一般只在必要的时候为类提供便利构造器。<br>
</p>
<p>便利构造器也采用相同样式的写法,但需要在 init 关键字之前放置 convenience 关键字,并使用空格将它们俩分开:<br>
</p>
<div class="blockcode">
<pre class="brush:cpp;">
convenience init(parameters) {
statements
}</pre>
</div>
<p><strong>类类型的构造器代理</strong><br>
</p>
<p>规则 1<br>
</p>
<p>指定构造器必须调用其直接父类的的指定构造器。<br>
</p>
<p>规则 2<br>
</p>
<p>便利构造器必须调用同类中定义的其它构造器。<br>
</p>
<p>规则 3<br>
</p>
<p>便利构造器最后必须调用指定构造器。<br>
</p>
<p>一个更方便记忆的方法是:</p>
<ul>
<li>指定构造器必须总是向上代理</li>
<li>便利构造器必须总是横向代理</li>
</ul>
<p>这些规则可以通过下面图例来说明:</p>
<p style="text-align: center"><img alt="" id="theimg" src="https://beijingoptbbs.oss-cn-hangzhou.aliyuncs.com/jb/2426819-f2c6fffde12d2d6df8db65fd15650099" style="font-size: 12px; border-top: 0px; font-family: "Microsoft Yahei", simsun, arial, sans-serif; border-right: 0px; white-space: normal; word-spacing: 0px; border-bottom: 0px; text-transform: none; font-weight: 400; color: rgb(51,51,51); padding-bottom: 0px; font-style: normal; text-align: left; padding-top: 0px; padding-left: 0px; border-left: 0px; orphans: 2; widows: 2; margin: 0px; letter-spacing: normal; padding-right: 0px; background-color: rgb(255,255,255) |
|