请参阅下面的简单代码片段。对于我的应用程序,我需要包含对CoreTelephony的一部分CTCellularPlanProvisioningRequest的调用。此类可用于iOS 12+。我在iOS 10和11上仍然有一个庞大的用户群需要支持,所以我将我的调用包含在'if #available`中,如下所示:
if #available(iOS 12, *) {
let cpProvioningRequest = CTCellularPlanProvisioningRequest()
print("cpProvioningRequest: \(cpProvioningRequest)")
} else {
print("No iOS 12+ available")
}不过,当我在运行iOS 11.4的模拟器设备上运行此代码时,应用程序会在加载阶段崩溃。在运行iOS 12或13的模拟器上,调用工作正常。当我用CTCellularPlanProvisioningRequest注释掉这两行代码时,这个应用程序可以在fine iOS 11上运行。
我在11.4上得到的错误是:
dyld: Symbol not found: _OBJC_CLASS_$_CTCellularPlanProvisioningRequest
Referenced from: /Users/frans/Library/Developer/CoreSimulator/Devices/25E0C601-5D38-4B41-A807-3575BC23AAB9/data/Containers/Bundle/Application/4DC30320-77F6-4107-A83A-EF0F5C5B464D/TestNewAPIOldiOS.app/TestNewAPIOldiOS
Expected in: /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 11.4.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
in /Users/frans/Library/Developer/CoreSimulator/Devices/25E0C601-5D38-4B41-A807-3575BC23AAB9/data/Containers/Bundle/Application/4DC30320-77F6-4107-A83A-EF0F5C5B464D/TestNewAPIOldiOS.app/TestNewAPIOldiOS我的线程视图显示:
dyld`__abort_with_payload:
0x10d2500d4 <+0>: movl $0x2000209, %eax ; imm = 0x2000209
0x10d2500d9 <+5>: movq %rcx, %r10
0x10d2500dc <+8>: syscall
-> 0x10d2500de <+10>: jae 0x10d2500e8 ; <+20>
0x10d2500e0 <+12>: movq %rax, %rdi
0x10d2500e3 <+15>: jmp 0x10d24e601 ; cerror_nocancel
0x10d2500e8 <+20>: retq
0x10d2500e9 <+21>: nop
0x10d2500ea <+22>: nop
0x10d2500eb <+23>: nop 我的问题是:如何确保此对CTCellularPlanProvisioningRequest的调用在iOS 12+上正常工作,同时不会对iOS<12造成崩溃或其他负面影响?
为了完整起见,下面是完整的类。部署目标设置为iOS 10。我知道CTCellularPlanProvisioningRequest的先决条件,在我的完整应用程序中,我已经填写了这些。我在Xcode11.2.1和Xcode10上进行了测试,我尝试了几个不同的设备模拟器,运行各种iOS12之前的版本。我无法在运行iOS 10或11的物理设备上运行此程序,因为我无法访问它们。
// TestNewAPIOldiOS
//
// Created by Frans Glorie on 15/11/2019.
// Copyright © 2019 Frans Glorie. All rights reserved.
//
import UIKit
import CoreTelephony
class ViewController: UIViewController {
// Functions
override func viewDidLoad() {
super.viewDidLoad()
let ctSubscriber = CTSubscriber()
print("Subscriber: \(ctSubscriber)")
if #available(iOS 12, *) {
let cpProvioningRequest = CTCellularPlanProvisioningRequest()
print("cpProvioningRequest: \(cpProvioningRequest)")
} else {
print("No iOS 12+ available")
}
}
}谢谢!
法兰斯
发布于 2019-12-08 19:44:26
我终于收到了苹果的回复,他们的建议似乎起作用了:“在审查了您的反馈后,我们为您提供了一些额外的信息。
在我们这方面有一个已知的问题,但您可以通过显式地与框架进行弱链接来解决这个问题。为此,请转到构建阶段,“将二进制文件与库链接”部分,在其中添加CoreTelephony,并将status字段从required设置为optional。这就修复了现在的崩溃。我们已经为你提到的问题提交了一个单独的bug,并将在未来的版本中修复它。谢谢!“
我试过了,它似乎工作得很好。还需要进一步的测试,但这似乎解决了问题。希望这对任何人都有帮助。
不幸的是,这里之前发布的解决方法都只解决了问题的一部分。
发布于 2019-11-23 20:40:18
由于CTCellularPlanProvisioningRequest是作为变通方法的NSObject子类,因此您可以使用iOS11及更低版本的failsafe通过obj-c动态运行时对其进行实例化
if let classAvailable = NSClassFromString("CTCellularPlanProvisioningRequest") {
let cpProvioningRequest = (classAvailable as! NSObject).init()
print("cpProvioningRequest: \(cpProvioningRequest)")
}这样做将不会在二进制文件中创建对_OBJC_CLASS_$_CTCellularPlanProvisioningRequest符号的依赖关系。
更新
在我的答案中的OP注释之后,下面是动态调用addPlan的代码。
if let requestClassAvailable = NSClassFromString("CTCellularPlanProvisioningRequest"),
let provisioningClassAvailable = NSClassFromString("CTCellularPlanProvisioning") {
if let cpProvisioningRequestType = requestClassAvailable as? NSObject.Type,
let cpProvisioningType = provisioningClassAvailable as? NSObject.Type {
let cpProvisioningRequest = cpProvisioningRequestType.init()
let cpProvisinioning = cpProvisioningType.init()
let addPlanSelector = NSSelectorFromString("addPlanWith:completionHandler:")
if let methodIMP = cpProvisinioning.method(for: addPlanSelector) {
unsafeBitCast(methodIMP,to:(@convention(c)(Any?,Selector,Any?,((UInt) -> Void)?)->Void).self)(cpProvisinioning, addPlanSelector, cpProvisioningRequest, nil)
}
}
}这还远不是完美的,但也许在你得到苹果的答复之前,它就足够了。
顺便说一句,我在我的答案here中介绍了Swift中动态调用的详细信息
https://stackoverflow.com/questions/58891940
复制相似问题