作者:无梗啦_671 | 来源:互联网 | 2023-01-27 14:37
我的项目使用Objective-C和Swift代码.当用户登录时,它会为用户首选项调用一组apis,我有一个DataCoordinator.swift类来调度API操作,我从UserDetailViewController.m类调用此类来加载用户首选项.在使用Xcode 9 beta 4将我的代码迁移到Swift 4之前,这种用法正常工作.现在当我登录时,我在DataCoordinator类中给出了这个错误.下面是我的DataCoordinator和Viewcontroller类的示例.
DataCoordinator.swift
import UIKit
@objcMembers
class DataCoordinator: NSObject {
//MARK:- Private
fileprivate var user = myDataStore.sharedInstance().user
fileprivate var preferenceFetchOperatiOns= [FetchOperation]()
fileprivate func scheduleFetchOperation(_ operation:FetchOperation, inFetchOperations operations:inout [FetchOperation]) {
guard operations.index(of: operation) == nil else { return }
operations.append(operation)
}
fileprivate func completeFetchOperation(_ fetchOperation:FetchOperation, withError error:Error?, andCompletionHandler handler:@escaping FetchCompletionHandler) {
func removeOperation(_ operation:FetchOperation, fromOperations operations:inout [FetchOperation]) {
if operations.count > 0 {
operations.remove(at: operations.index(of: fetchOperation)!)
handler(error)
}
}
if preferenceFetchOperations.contains(fetchOperation) {
removeOperation(fetchOperation, fromOperations: &preferenceFetchOperations)
}
}
fileprivate func schedulePreferencesFetchOperation(_ serviceName:String, fetch:@escaping FetchOperationBlock){
let operation = FetchOperation(name: serviceName, fetch: fetch);
scheduleFetchOperation(operation, inFetchOperations: &preferenceFetchOperations)
}
fileprivate func runOperationsIn(_ fetchOperations:inout [FetchOperation]) {
for var operation in fetchOperations {
guard operation.isActivated == false else { continue }
operation.isActivated = true
operation.execute()
}
}
//MARK:- Non-Private
typealias FetchCompletiOnHandler= (_ error:Error?)->Void
var numberOfPreferencesFetchCalls:Int {
get { return preferenceFetchOperations.count }
}
// MARK: -
func fetchPreferences(_ completionHandler:@escaping FetchCompletionHandler) -> Void {
defer {
runOperationsIn(&preferenceFetchOperations)
}
schedulePreferencesFetchOperation("com.fetchPreferences.type1") {[unowned self] (operation:FetchOperation) in
WebServiceManager.getType1Detail(for: user) {[unowned self] (error) in
self.completeFetchOperation(operation, withError: error, andCompletionHandler: completionHandler)
}
}
schedulePreferencesFetchOperation("com.fetchPreferences.type2") {[unowned self] (operation:FetchOperation) in
WebServiceManager.getType2Detail(for: user) {[unowned self] (error) in
self.completeFetchOperation(operation, withError: error, andCompletionHandler: completionHandler)
}
}
schedulePreferencesFetchOperation("com.fetchPreferences.type3") {[unowned self] (operation:FetchOperation) in
WebServiceManager.getType3Detail(for: user) {[unowned self] (error) in
self.completeFetchOperation(operation, withError: error, andCompletionHandler: completionHandler)
}
}
schedulePreferencesFetchOperation("com.fetchPreferences.type4") {[unowned self] (operation:FetchOperation) in
WebServiceManager.getType4Detail(for: user) {[unowned self] (error) in
self.completeFetchOperation(operation, withError: error, andCompletionHandler: completionHandler)
}
}
}
}
// MARK:- Fetch Operation Struct
private typealias FetchOperatiOnBlock= (_ operation:FetchOperation)->Void
private struct FetchOperation:Hashable {
fileprivate var runToken = 0
fileprivate let fetchBlock:FetchOperationBlock
let name:String!
var isActivated:Bool {
get {
return runToken == 0 ? false : true
}
mutating set {
if runToken == 0 && newValue == true {
runToken = 1
}
}
}
fileprivate var hashValue: Int {
get {
return name.hashValue
}
}
func execute() -> Void {
fetchBlock(self)
}
init (name:String, fetch:@escaping FetchOperationBlock) {
self.name = name
self.fetchBlock = fetch
}
}
private func ==(lhs: FetchOperation, rhs: FetchOperation) -> Bool {
return lhs.hashValue == rhs.hashValue
}
//我在viewcontrollers viewDidLoad方法中调用它
__weak UserDetailViewController *weakSelf = self;
[self.dataCoordinator fetchPreferences:^(NSError * _Nullable error) {
if (error == nil) {
[weakSelf didFetchPrefrences];
}
else {
// handle error
}
}];
//completion response
- (void)didFetchPrefrences {
//when api calls complete load data
if (self.dataCoordinator.numberOfPreferencesFetchCalls == 0) {
//Load details
}
}
我不知道如何继续这个,我在https://bugs.swift.org/browse/SR-5119上看到了一个错误报告, 但它似乎已在Xcode 9 beta 3中修复.任何帮助表示赞赏
1> Mark Bridges..:
我认为这个'bug'可能是一个Swift 4'功能',特别是他们称之为'独家访问内存'.
看看这个WWDC视频.大约50分钟后,这位长发演讲者解释道.
https://developer.apple.com/videos/play/wwdc2017/402/?time=233
如果您乐意忽略它,可以尝试在方案设置中关闭线程消毒剂.但是,调试器试图告诉你一个微妙的线程问题,所以它可能更好地利用你的时间来弄清楚为什么你在读取数据的同时写入了你的数组.
这有一些非凡的微妙之处.首先,如果你在一个struct的属性上有一个didSet观察者,*write access仍然被强制为active*.其次,如果这是在类的属性上,则由于某些引用该问题的引用语义而不强制执行*写访问*.最后,如果突变发生*通过*协议类型,它*取决于*协议类型是否为"类"绑定.如果没有,它就会以`struct`级别的限制行事,即使运行时类型毕竟是引用类型.我不希望ppl跟随w/o示例代码.
2> 小智..:
在目标的构建设置下.选择No Enforcement
了Exclusive Access to Memory
从Swift Compiler - Code Generation
请注意,这不会自动修复实际的数据争用问题.但是,线程清理程序可能会发现误报 - 如果变异函数有自己的同步原语设置,通常就是这种情况.
3> Ralf Hundewa..:
仅适用于Swift 4以及使用.initial
KVO设置选项时
如果在observeValue方法中检查上下文,只需将上下文变量设置为静态.这篇博文详细描述了这个bug.
4> J. Doe..:
在Swift 5.0中,这是在发布模式下运行应用程序时的默认行为。在5.0之前(今天为Swift 4.2.1及更低版本),此行为仅在调试模式下运行。
如果您忽略此错误,则您的应用程序将在发布模式下失败。
考虑以下示例:
func modifyTwice(_ value: inout Int, by modifier: (inout Int) -> ()) {
modifier(&value)
modifier(&value)
}
func testCount() {
var count = 1
modifyTwice(&count) { $0 += count }
print(count)
}
打印print(count)行时count的值是多少?好吧,我也不知道,当您运行此代码时,编译器会给出不可预测的结果。在Swift 4.0中,在调试模式下是不允许的,在Swift 5.0中,即使在运行时,它也会崩溃。
资料来源:https : //swift.org/blog/swift-5-exclusivity/