简体   繁体   中英

Prevent Obj-C code from passing `nil` to a Swift method with non-optional parameters

I'm dealing with an issue where Obj-C code can pass nil to Swift methods that do not take Optional parameters.

I'm looking for a way for Xcode compiler to prevent this. It seems that all nullability annotations are for Swift to call Objc, not vice-versa.

Some example code:

// 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)
  }
}

Then in Obj-c, we call the Swift method passing in an optional value, with nil :

// file3.m
[Date.new doSomethingWithOtherDate:[Foo.new date]];

Is there a way to prevent this? How can we tell the compiler doSomethingWithOtherDate does not take nil .

Follow up

Following up on this issue, passing a literal nil on the call triggers a warning, which is very helpful. However, passing a nil value does not trigger anything. The static analyzer does however find the issue if the nil reference is local.

However, if a nil reference is passed via another object, the static analyzer fails to identify this issue.

Objc - Swift 崩溃

I've made this repo to showcase this issue: https://github.com/eneko/ObjcCrashDemo

In general, no. Objective-C does not have enough understanding of nullability to detect every case.

If you explicitly pass nil you should get a warning "Null passed to a callee that requires a non-null argument". You can turn this into an error by adding -Werror=nonnull to "Other Warning Flags" in your build settings.

However this is extremely limited. Even something as simple as NSDate* date = nil;and passing that will produce no warning/error. Even adding a nonnull property in Objective-C and passing that will not produce a warning/error.

I was able to find this article: https://fabiancanas.com/blog/2020/1/9/swift-undefined-behavior.html (search for the "Detecting nil when nil isn't possible" section). This article discusses a rather complex solution to the problem, but I would suggest the easier solution would be to declare your function argument type as Date! (either a value or nil) and then perform a guard let on the passed value before proceeding in the function body:

// file2.swift
extension Date {
  @objc(doSomethingWithOtherDate:)
  func doSomething(with otherDate: Date!) {
    guard let od = otherDate else {
      // do something else
      return
    }
    print(date)
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM