在 Swift 中检查未初始化的非可选值?

Check for uninitialized non optional value in Swift?

提问人:user6631314 提问时间:11/12/2020 最后编辑:user6631314 更新时间:11/12/2020 访问量:373

问:

我有一些代码已经运行了很长时间,但突然导致运行时崩溃。这可能是因为它第一次获得零值(由于一些故障或其他原因),但无论如何,我的应用程序现在在启动时崩溃。

代码为:

 @objc func download(surl: NSString, completion : @escaping (NSData) -> Void ) {
print("surl to download is:",surl)//CRASHES HERE if surl is uninitialized.

它在错误指示的行上崩溃:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)

下面是在运行时发生错误的控制台的屏幕截图enter image description here

这是调试器中的行,它说它在崩溃时是“单一化的”。

enter image description here如果 surl 是非可选的,是否有任何方法可以检查未初始化?

我不愿意将 surl 更改为可选,因为此方法在应用程序中称为无数个位置,我担心在方法中更改它会导致其他东西中断。

所以想找出一种方法来测试 surl 是否未初始化,如果可能的话返回。

感谢您的任何建议。

编辑:

我已经在调用上述方法的方法中尝试了非零测试并通过非零测试。但是,如果您尝试用它做某事,它就会崩溃。此外,在崩溃后在控制台中打印它会产生:(NSString) $R2 = <uninitialized>

这是屏幕截图:

enter image description here

ios swift string null 选项类型

评论

0赞 Leo Dabus 11/12/2020
你确定你的代码在那里崩溃了吗?为什么首先使用 NSString/NSData?
0赞 vadian 11/12/2020
随着你的实现不能是.如果是,编译器会(在编译时)抱怨。这就是 Swift 强类型系统的好处。错误一定在其他地方。正如 Leo 已经提到的,如果有原生的 Swift 对应物,请停止使用类。surlnilNS...
0赞 user6631314 11/12/2020
@Leo Dabus,看起来代码在那里崩溃了。看截图。使用 NSString,因为这主要是一个 Objective-C 项目。但是,一些函数(例如此函数)是用 Swift 编写的
0赞 user6631314 11/12/2020
@vadian。重新检查控制台,显示“未初始化”。如前所述,此代码已在生产应用中运行了一年多,但不知何故,无效值已经悄悄出现。不知道怎么做。可能是崩溃或其他什么。将问题编辑为如何检查未初始化。归根结底,我需要测试单位化值,即使它们非常罕见,因为该错误会阻止应用程序工作。

答:

1赞 Cristik 11/12/2020 #1

可能是你的 Swift 代码是从 Objective-C 调用的,而 Objective-C 向 Swift 发送 nil。这可以解释为什么你会在链的末端崩溃,只有在实际使用值的地方才会崩溃。

请注意,Objective-C 会很乐意将 nil 值传递给不接受 nil 的 Swift 函数,并且 Swift 也会很乐意将1 该 nil 值转发到只允许非 nil 值的地方,只有在它实际尝试使用该值的地方才会崩溃。

从我在堆栈跟踪中看到的内容来看,这似乎是问题所在:Objective-C 方法调用 Swift 方法,该方法将调用转发给 ,并且当尝试使用该值时,崩溃发生在链中的最后一个函数上。-[IDModel updateCa..]Utilities.downloadImag...Utilities.download(surl..

为了将更改的影响降到最低,您可以做的是“重载”函数,一个是 Objective-C 世界的可选函数,另一个是 Swift 世界的函数:surl

@objc(download) func objcDownload(surl: NSString?, completion : @escaping (NSData) -> Void ) {
    guard let surl = surl else {
        // handle this problems
        return
    }
    // forward to the worker function
    download(surl: surl, completion: completion)
}

// @nonobjc might not be needed, depending of the class declaration
// but being explicit doesn't hurt
@nonobjc func download(surl: NSString, completion : @escaping (NSData) -> Void ) {
    // same code, no changes needed
}

这边:

  • Objective-C 代码将看到相同的功能,无需更改
  • Objective-C 桥接的 Swift 实现可以检查 nils
  • Swift 代码仍然可以使用,现在下载功能。@nonobjc

如果你不相信我从 Objective-C 传递给 Swift 的 nil 值,这里有一个例子来证明这一点:

// in a header file, imported in the bridging header
@interface ObjCWorker: NSObject

+ (void)sendNilToSwift;

@end

// in the corresponding .m file
@implementation ObjCWorker

+ (void)sendNilToSwift {
    NSString *shouldNotBeNil = nil;
    [SwiftWorker doWorkWithNonNull:shouldNotBeNil];
}
@end
// in a Swift file
class SwiftWorker: NSObject {
    
    @objc static func doWork(with string: NSString) {
        delegateWorkToSomeOtherMethod(string)
    }
    
    static func delegateWorkToSomeOtherMethod(_ string: NSString) {
        delegateWorkToAnotherMethod(string)
    }
    
    static func delegateWorkToAnotherMethod(_ string: NSString) {
        print(string)
    }
}

现在,只需编写 ,你就会在行动中遇到完全相同的崩溃。ObjCWorker.sendNilToSwift()

1请注意,这目前仅适用于 Objective-C 参数:NSString、NSData、...、桥接的 Swift 类型(如 String、Data)在从 Objective-C 端作为 nil 值发送时以某种方式初始化为空值。

评论

0赞 matt 11/12/2020
但是,OP 首先将这些参数指定为 Objective-C 类型,这难道不是错误的吗?NSString 和 NSData 是错误的。
0赞 Cristik 11/12/2020
@matt同意,当字符串工作也可以工作(甚至可能更好)时,使用 NSString 感觉是错误的。也许OP继承了这个代码库,不想过多地接触它。
0赞 user6631314 11/13/2020
感谢您的出色解释,您是对的,从 Obj-C 方法调用该方法时失败。NSString 和 NSData 的原因是 Objective-C 方法在调用方法中向它发送一个字符串 (NSString,然后使用返回的 NSData。在函数(未显示代码)中,它确实使用字符串和数据。