首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >来自Objective类的非指定初始化项继承

来自Objective类的非指定初始化项继承
EN

Stack Overflow用户
提问于 2015-07-01 12:20:20
回答 1查看 278关注 0票数 3

在对UIKit类进行子类分类并向它们添加不可变变量时,我遇到了一些问题,因此我做了一个测试项目来了解发生了什么。

我的结论是,如果:

  • 我们有一个Objective类,它继承于另一个类,具有自己指定的初始化项(隐式或显式注释)。
  • 在它的初始化器中,它调用[self initWithXX],其中initWithXX是超类上的init方法。
  • 我们在Swift中子类这个类,添加一个不可变的属性(它显然必须在实例化时初始化)
  • 我们为这个Swift类实现一个指定的初始化器,它设置不可变属性,然后调用父类指定的初始化项

然后,这将导致运行时异常,因为Swift类在调用Objective超类的指定初始化项时,将尝试在self上调用self,而且由于我们已经实现了指定的初始化器,因此该方法没有从超类继承。

此测试的代码为:

View.h

代码语言:javascript
复制
#import <UIKit/UIKit.h>

@interface View : UIView

-(instancetype)initWithIdentifier:(NSString *)identifier;

@end

View.m

代码语言:javascript
复制
#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

代码语言:javascript
复制
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")
    }
}

异常生成代码

代码语言:javascript
复制
let view: SwiftView = SwiftView(name: "test")

异常

致命错误:类'Test.SwiftView‘使用未实现的初始化器’init(框架:)‘

我的动机是分类标准库类(UITableViewControllerMKAnnotationView.)并添加不可变属性以包含注入的依赖项,然后需要在子类实例化时通过将它们传递到自定义指定的init方法来初始化这些属性,并发现这会导致上述意外异常。

在做了这个实验来确定原因之后,我想我理解为什么会发生这种情况(因为父对象--C类指定的初始化器正在委托给它自己的另一个指定的初始化器,而不是调用它的超类的初始化器)。显然,要解决这个问题,标准库应该更改它们的initialiser方法来调用超类初始化器,而不是self初始化程序,但我可以理解调用self而不是super可能是有效的功能,以便允许子类覆盖默认行为,同时保留特定的初始化符。而且,我可以想象这样的变化可能会造成很多问题,因为很多应用程序可能都建立在这个功能的基础上,而这会破坏它。我认为这里的基本问题是Objective和Swift之间的语言特性不兼容。

除了(非常不受欢迎的)使这些不可变变量可变并将它们设置在第二个初始化阶段之外,还有人能找到解决这个问题的方法(无论是在Apple修复还是代码解决方案方面)吗?

编辑

下面是我的测试项目的堆栈跟踪:

代码语言:javascript
复制
#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项目,我试图在视图控制器的viewDidLoad方法中实例化我的SwiftView对象。

编辑

作为额外的问题,另一个结果是,如果为了使子类正确实例化,我要将Swift类变量更改为隐式未包装的可选选项:

代码语言:javascript
复制
let name: String!

并通过将我的不可变名称变量设置为nil来实现缺失的初始化项(这样我就可以使用我创建的指定初始化项,但是如果有人使用了我必须实现但从未使用的指定初始化项,那么由此产生的崩溃就是他们的错误),在我用name参数实例化SwiftView对象之后,我得到了一个完全出乎意料的结果。

代码语言:javascript
复制
let view: SwiftView = SwiftView(name: "test")

view.name的值是nil (大概是因为init(frame:)初始化是在init(name:)方法最初设置name之后调用的)。

编辑

雷达发布到苹果

EN

回答 1

Stack Overflow用户

发布于 2020-01-04 14:03:16

如果不需要在Objective代码中覆盖initWithFrame:,那么只需调用[super initWithFrame:CGRectZero]而不是[self initWithFrame:CGRectZero]。这将指示编译器查找基本的UIView实现,而不是后代clas中的实现。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31161143

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档