防止 Obj-C 代码将 `nil` 传递给具有非可选参数的 Swift 方法
Posted
技术标签:
【中文标题】防止 Obj-C 代码将 `nil` 传递给具有非可选参数的 Swift 方法【英文标题】:Prevent Obj-C code from passing `nil` to a Swift method with non-optional parameters 【发布时间】:2020-07-17 17:34:43 【问题描述】:我正在处理 Obj-C 代码可以将 nil
传递给不采用可选参数的 Swift 方法的问题。
我正在为 Xcode 编译器寻找一种方法来防止这种情况发生。似乎所有可空性注释都是为了让 Swift 调用 Objc,而不是反之亦然。
一些示例代码:
// file1.swift
@objc
class Foo: NSObject
@objc let date: Date? // default to nil
...
// file2.swift
extension Date
@objc(doSomethingWithOtherDate:)
func doSomething(with otherDate: Date)
print(date)
然后在Obj-c中,我们调用Swift方法传入一个可选值,nil
:
// file3.m
[Date.new doSomethingWithOtherDate:[Foo.new date]];
有没有办法防止这种情况发生?我们如何告诉编译器doSomethingWithOtherDate
不接受nil
。
跟进
跟进此问题,在调用中传递文字 nil
会触发警告,这非常有帮助。但是,传递 nil
值不会触发任何事情。但是,如果 nil 引用是本地的,则静态分析器会发现问题。
但是,如果 nil
引用通过另一个对象传递,则静态分析器无法识别此问题。
我制作了这个 repo 来展示这个问题:https://github.com/eneko/ObjcCrashDemo
【问题讨论】:
【参考方案1】:一般来说,不会。 Objective-C 对可空性没有足够的理解来检测每个案例。
如果您明确传递 nil,您应该会收到警告“Null 传递给需要非 null 参数的被调用者”。您可以通过将 -Werror=nonnull
添加到构建设置中的“其他警告标志”来将其变为错误。
但是,这是极其有限的。即使是像NSDate* date = nil;
这样简单的东西,通过它也不会产生警告/错误。即使在 Objective-C 中添加 nonnull
属性并传递它也不会产生警告/错误。
【讨论】:
感谢您的提示。我试过添加标志,但正如你提到的,在这种情况下它没有帮助。这是一个无赖。看来,如果没有办法阻止这种行为,最安全的方法是让所有带有@objc
标记的 Swift 方法只有可选参数。远非理想:/【参考方案2】:
我找到了这篇文章:https://f***canas.com/blog/2020/1/9/swift-undefined-behavior.html(搜索“当 nil 不可能时检测 nil”部分)。本文讨论了该问题的一个相当复杂的解决方案,但我建议更简单的解决方案是将您的函数参数类型声明为Date!
(值或零),然后在继续之前对传递的值执行guard let
在函数体中:
// file2.swift
extension Date
@objc(doSomethingWithOtherDate:)
func doSomething(with otherDate: Date!)
guard let od = otherDate else
// do something else
return
print(date)
【讨论】:
这种方法的危险在于,当您从 Swift 调用此代码时,您将失去 Swift 类型检查器的优势,当您意外传递 nil 时,可能会将编译时错误变成运行时错误 是的。为此,最好将参数设置为可选:Date?
,这对于 Obj-C 和 Swift 都是安全的。但是,不能解决发布的问题,这是关于非可选参数的问题。
问题在于 Swift -> ObjC 之间的内在兼容性。换一种方式(在 Swift 中调用 ObjC 函数),可以将 ObjC 函数参数注释为非空,这在 Swift 中正确声明参数类型为非可选。根据文章,如果没有某种黑客解决方法,就无法解决这种情况。因此,您的选择是使用本文中的解决方法,或者通过将 swift 参数类型声明为 !或?。以上是关于防止 Obj-C 代码将 `nil` 传递给具有非可选参数的 Swift 方法的主要内容,如果未能解决你的问题,请参考以下文章
Obj-C 框架返回 nil,并让我的 Swift 代码崩溃,说“致命错误:在展开可选值时意外发现 nil”
Obj-C 框架返回 nil,并让我的 Swift 代码崩溃,说“致命错误:在展开可选值时意外发现 nil”