来自Objective-C类的非指定的初始化程序继承

问题描述 投票:3回答:1

[子类化UIKit类并向其添加不可变变量时遇到了问题,我进行了一个测试项目以弄清发生了什么。

我的结论是,如果:

  • 我们有一个Objective-C类,它从另一个类继承而来,带有自己的指定的初始化程序(隐式或显式注释)]
  • 在它的[[初始化器中,它调用[self initWithXX],其中initWithXX是超类上的init方法]
  • 我们在
  • Swift
  • 中将该类子类化,添加了一个不可变的属性(显然必须在实例化时初始化)我们为此
  • Swift
  • 类实现单个指定的初始化器,该类设置了不可变的属性,然后调用父类指定的初始化器
然后,这将导致运行时异常,因为

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:)'

我这样做的动机是对标准库类(UITableViewControllerMKAnnotationView ...)进行子类化,并添加不可变属性以包含对它们的注入依赖关系,然后需要在子类实例化时通过以下方式对其进行初始化:将它们传递给指定的自定义init方法,并发现这导致了上述意外的异常。

已经完成了确定原因的实验,我想我理解为什么会发生(因为父类

Objective-C

指定的初始化程序委托给它的另一个指定的初始化程序而不是调用其超类的initialiser)。似乎可以解决此问题,标准库应该更改其initialiser方法以调用超类initialisers而不是self initialisers,但我可以体会到调用self super的有效值可能是有效的功能,以便允许子类在保留特定initialisers的同时覆盖默认行为。另外,我想这样的更改可能会引起很多问题,因为许多应用程序可能都基于此功能构建的,而这会破坏它。我想这里的基本问题是Objective-CSwift之间的语言功能不兼容。除了使这些不可变变量可变并在第二个初始化阶段设置它们的(非常不希望的)方式之外,没有人能看到解决此问题的任何方法(从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类并向其添加不可变变量时遇到了问题,我制作了一个测试项目来弄清楚发生了什么。我的结论是,如果:我们有一个...
objective-c swift inheritance designated-initializer
1个回答
0
投票
© www.soinside.com 2019 - 2024. All rights reserved.