[子类化UIKit
类并向其添加不可变变量时遇到了问题,我进行了一个测试项目以弄清发生了什么。
我的结论是,如果:
[self initWithXX]
,其中initWithXX
是超类上的init
方法]Swift
类在调用Objective-C超类的指定初始化程序时,将尝试在initWithXX
和此方法上调用self
尚未从超类继承,因为我们已经实现了指定的初始化程序。此测试的代码为:View.h
#import <UIKit/UIKit.h>
@interface View : UIView
-(instancetype)initWithIdentifier:(NSString *)identifier;
@end
View.m
#import "View.h"
@implementation View
-(instancetype)initWithIdentifier:(NSString *)identifier {
self = [self initWithFrame:CGRectZero];
if (self) {
if ([identifier length] > 0) {
return self;
}
}
return nil;
}
-(instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
return self;
}
return nil;
}
@end
SwiftView.swift
import Foundation
import UIKit
class SwiftView: View {
let name: String
init(name: String) {
self.name = name
super.init(identifier: "test")
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
异常生成代码
let view: SwiftView = SwiftView(name: "test")
Exception
致命错误:对类'Test.SwiftView'使用未实现的初始化程序'init(frame:)'
我这样做的动机是对标准库类(UITableViewController
,MKAnnotationView
...)进行子类化,并添加不可变属性以包含对它们的注入依赖关系,然后需要在子类实例化时通过以下方式对其进行初始化:将它们传递给指定的自定义init
方法,并发现这导致了上述意外的异常。
已经完成了确定原因的实验,我想我理解为什么会发生(因为父类
Objective-C
类指定的初始化程序委托给它的另一个指定的初始化程序而不是调用其超类的initialiser)。似乎可以解决此问题,标准库应该更改其initialiser方法以调用超类initialisers而不是self
initialisers,但我可以体会到调用self
super
的有效值可能是有效的功能,以便允许子类在保留特定initialisers的同时覆盖默认行为。另外,我想这样的更改可能会引起很多问题,因为许多应用程序可能都基于此功能构建的,而这会破坏它。我想这里的基本问题是Objective-C和Swift之间的语言功能不兼容。除了使这些不可变变量可变并在第二个初始化阶段设置它们的(非常不希望的)方式之外,没有人能看到解决此问题的任何方法(从Apple修复或代码变通方法而言)?编辑
这是我的测试项目中的堆栈跟踪:#0 0x000000010b0c9f78 in Test.SwiftView.init (Test.SwiftView.Type)(frame : C.CGRect) -> Test.SwiftView at /Users/xxx/Documents/Test/Test/SwiftView.swift:12
#1 0x000000010b0c9fa0 in @objc Test.SwiftView.init (Test.SwiftView.Type)(frame : C.CGRect) -> Test.SwiftView ()
#2 0x000000010b0c37bb in -[View initWithIdentifier:] at /Users/xxx/Documents/Test/Test/View.m:14
#3 0x000000010b0c97a6 in Test.SwiftView.init (Test.SwiftView.Type)(name : Swift.String) -> Test.SwiftView at /Users/xxx/Documents/Test/Test/SwiftView.swift:18
#4 0x000000010b0c9914 in Test.SwiftView.__allocating_init (Test.SwiftView.Type)(name : Swift.String) -> Test.SwiftView ()
#5 0x000000010b0c3b0e in Test.ViewController.viewDidLoad (Test.ViewController)() -> () at /Users/xxx/Documents/Test/Test/ViewController.swift:18
#6 0x000000010b0c3be2 in @objc Test.ViewController.viewDidLoad (Test.ViewController)() -> () ()
#7 0x000000010bbcb1d0 in -[UIViewController loadViewIfRequired] ()
#8 0x000000010bbcb3ce in -[UIViewController view] ()
#9 0x000000010bae6289 in -[UIWindow addRootViewControllerViewIfPossible] ()
#10 0x000000010bae664f in -[UIWindow _setHidden:forced:] ()
#11 0x000000010baf2de1 in -[UIWindow makeKeyAndVisible] ()
#12 0x000000010ba96417 in -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] ()
#13 0x000000010ba9919e in -[UIApplication _runWithMainScene:transitionContext:completion:] ()
#14 0x000000010ba98095 in -[UIApplication workspaceDidEndTransaction:] ()
#15 0x000000010f84c5e5 in __31-[FBSSerialQueue performAsync:]_block_invoke_2 ()
#16 0x000000010d7df41c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ ()
#17 0x000000010d7d5165 in __CFRunLoopDoBlocks ()
#18 0x000000010d7d4f25 in __CFRunLoopRun ()
#19 0x000000010d7d4366 in CFRunLoopRunSpecific ()
#20 0x000000010ba97b02 in -[UIApplication _run] ()
#21 0x000000010ba9a8c0 in UIApplicationMain ()
#22 0x000000010b0c90a7 in main at /Users/xxx/Documents/Test/Test/AppDelegate.swift:12
#23 0x000000010e54f145 in start ()
#24 0x000000010e54f145 in start ()
很明显,我有一个简单的单视图iOS项目,并且尝试在的view controller
SwiftView
方法中实例化viewDidLoad
对象。编辑
作为额外的问题,这的另一个结果是,如果试图使我的子类正确实例化,那么我要将我的[[Swift类变量更改为一个隐式展开的可选变量:let name: String!
并通过将我的不可变name
变量设置为nil
来实现丢失的
initialiser(这样我就可以使用我创建的指定初始化器,但是如果有人使用指定初始化器] 我必须实现但从未使用过,因此导致的崩溃是他们的错),我得到了完全出乎意料的结果,即我在[]中使用SwiftView
参数实例化了name
对象之后let view: SwiftView = SwiftView(name: "test")
view.name
的值为nil
(大概是因为在init(frame:)
方法最初设置了init(name:)
之后调用了name
initializer)。
编辑
[Radar发布到Apple
在子类化UIKit类并向其添加不可变变量时遇到了问题,我制作了一个测试项目来弄清楚发生了什么。我的结论是,如果:我们有一个...