同时访问0x1c0a7f0f8,但修改需要Xcode 9 beta 4上的独占访问错误

时间:2017-07-31 12:25:16

标签: swift crash swift4 ios11 xcode9-beta

我的项目使用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中修复。感谢任何帮助

12 个答案:

答案 0 :(得分:20)

我认为这个' bug'可能是一个Swift 4'功能,特别是他们称之为“独占访问内存”的功能。

查看此WWDC视频。大约50分钟后,这位长发演讲者解释道。

https://developer.apple.com/videos/play/wwdc2017/402/?time=233

您可以尝试在方案设置中关闭线程清理器,如果您乐意忽略它。但是,调试器试图告诉你一个微妙的线程问题,所以它可能更好地利用你的时间来弄清楚为什么你有同时写入你的阵列的东西#&# 39;正在阅读。

答案 1 :(得分:12)

在目标的构建设置下。从No Enforcement选择Exclusive Access to Memory Swift Compiler - Code Generation

答案 2 :(得分:9)

仅适用于Swift 4以及使用.initial选项进行KVO设置

如果您在observeValue方法中检查上下文,只需将您的上下文变量静态。这个blog post详细描述了这个错误。

答案 3 :(得分:3)

Swift 5.0 中,这是在发布模式中运行应用程序时的默认行为。在5.0之前(从今天的4.1.2版开始,以及更低版本),只有在调试模式下,此行为才会运行。

如果您忽略此错误,则您的应用程序可能会在发布模式下失败。

考虑以下示例:

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/

答案 4 :(得分:2)

就我而言,Swift 4实际上发现了一种我不会注意到的错误,直到我开始从多个地方调用一个函数。我的函数传递了一个inout全局数组,它引用了该参数和全局名称。当我将功能更改为仅引用参数时,“同时访问”错误消失了。

答案 5 :(得分:2)

在这里快速5。 我从属性didSet调用一个函数,并从同一对象测试另一个属性,但出现此错误。

我通过在另一个线程中调用函数来修复它:

DispatchQueue.global(qos: .userInitiated).async {
    // do something
}

基本修复,但是有效。

答案 6 :(得分:2)

为我修复的是将 lazy 添加到 var

答案 7 :(得分:1)

就我而言,我在构建项目的过程中更改了桌子的高度。那时我的设备已与网络连接。我删除了导出的数据,它为我解决了这个问题。

答案 8 :(得分:0)

numberOfSections覆盖函数中返回零将导致此崩溃:

override func numberOfSections(in collectionView: UICollectionView) -> Int {
    // This causes a crash!    
    return 0
}

简单的解决方案-在上面的函数中使用return 1,然后在return 0函数中使用collectionView(_:numberOfItemsInSection:)

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int

答案 9 :(得分:0)

我要做的是将FetchOperation更改为class,而不是struct

答案 10 :(得分:0)

@Mark Bridges和@ geek1706的答案是很好的答案,但我想在这件事上加2美分,并举一个一般的例子。

如上所述,这是Swift 4 SE-176中的功能。

  

当然,仍然应该允许实现检测并发冲突的访问。某些程序员可能希望至少在某些构建配置中使用选择加入的线程安全实施机制。

排他访问是强制执行的,在访问该变量时,var的每个写突变都必须是排他的。在多线程环境中,访问共享变量的多个线程和一个或多个可以修改它。

没有什么比一个好例子更好的了: 如果我们尝试使用2个对象之间的抽象(在协议类型上发生突变)在多线程环境中改变共享值,并且Exclusive Access to Memory处于打开状态,则我们的应用程序将崩溃。

protocol Abstraction {
  var sharedProperty: String {get set}
}

class MyClass: Abstraction {
  var sharedProperty: String

  init(sharedProperty: String) {
     self.sharedProperty = sharedProperty
  }

  func myMutatingFunc() {
     // Invoking this method from a background thread
     sharedProperty = "I've been changed"
  }
}


class MainClass {
   let myClass: Abstraction

   init(myClass: Abstraction) {
     self.myClass = myClass
   }

   func foobar() {
      DispatchQueue.global(qos: .background).async {
         self.myClass.myMutatingFunc()
      }
   }
}

let myClass = MyClass(sharedProperty: "Hello")
let mainClass = MainClass(myClass: myClass)
// This will crash
mainClass.foobar()

由于我们未声明Abstraction协议受class约束,因此在运行时,在myMutatingFunc内部,对self的捕获将被视为{{1 }},即使我们注入了实际的structclass)。

  

转义变量通常需要动态执行,而不是动态执行   静态执行。这是因为Swift无法推理何时   转义的闭包将被调用,因此何时将变量   已访问。

解决方案是将MyClass协议绑定到Abstraction

class

答案 11 :(得分:0)

当我根据搜索查询修改谓词并重新加载表时,这种情况发生在我身上,以便其重新进入(访问)同一类对象代码。请检查我的堆栈跟踪及其设置的“ searchPhrase”,并再次从数据库工作器触发表重新加载,然后又重新返回到数据库工作器(用于reloadData之后的项目计数)。

0    libswiftCore.dylib                 0x000000010a325590 swift_beginAccess + 568
1    Groupe                             0x00000001063d5cf0 SCPickUsersInteractor.dbWorker.getter + 59
2    Groupe                             0x00000001063d5830 SCPickUsersInteractor.count.getter + 58
3    Groupe                             0x00000001063d8550 protocol witness for SCPickUsersInteractorProtocol.count.getter in conformance SCPickUsersInteractor + 14
4    Groupe                             0x0000000105a340d0 SCPickUsersViewController.tableView(_:numberOfRowsInSection:) + 278
5    Groupe                             0x0000000105a34280 @objc SCPickUsersViewController.tableView(_:numberOfRowsInSection:) + 76
6    UIKitCore                          0x00007fff482a3537 -[UITableView _numberOfRowsInSection:] + 62
7    UIKitCore                          0x00007fff482b3c42 -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 1938
8    UIKitCore                          0x00007fff482b85cd -[UITableViewRowData numberOfRows] + 67
9    UIKitCore                          0x00007fff4827362d -[UITableView noteNumberOfRowsChanged] + 117
10   UIKitCore                          0x00007fff48271b8b -[UITableView reloadData] + 1426
11   Groupe                             0x0000000105a35890 SCPickUsersViewController.reloadTableView() + 152
12   Groupe                             0x0000000105a37060 protocol witness for SCListUpdatesProtocol.reloadTableView() in conformance SCPickUsersViewController + 9
13   Groupe                             0x00000001068a14f0 SCPickUsersPresenter.reloadTableView() + 158
14   Groupe                             0x00000001068a2350 protocol witness for SCListUpdatesProtocol.reloadTableView() in conformance SCPickUsersPresenter + 17
15   Groupe                             0x0000000105a8bc90 SCPickUsersDBWorker.searchPhrase.didset + 911
16   Groupe                             0x0000000105a8c0e0 SCPickUsersDBWorker.searchPhrase.setter + 356
17   Groupe                             0x0000000105a8ffb0 protocol witness for SCFRCProtocol.searchPhrase.setter in conformance SCPickUsersDBWorker + 37
18   Groupe                             0x00000001063d6500 SCPickUsersInteractor.searchPhrase.setter + 274
19   Groupe                             0x00000001063d8630 protocol witness for SCPickUsersInteractorProtocol.searchPhrase.setter in conformance SCPickUsersInteractor + 17
20   Groupe                             0x0000000105a34eb0 SCPickUsersViewController.searchBar(_:textDidChange:) + 322
21   Groupe                             0x0000000105a35020 @objc SCPickUsersViewController.searchBar(_:textDidChange:) + 105

解决方案为我工作: 几毫秒或半秒后,从数据库工作者类中称为“ reloadData”。