Memory Access Crash ONLY in Archived Cocoa Application (EXC_BAD_ACCESS/EXC_I386_GPFLT)

送分小仙女□ 提交于 2020-01-17 01:45:12

问题


I created my first Cocoa App for OS X and was happy to finally submit it to iTunes connect. Just to get not approved because the application is crashing. I had a hard time to figure out all things until here and now I'm finally stuck and don't know how to solve the following issue:

First of all, the application works perfectly fine when simply built and run. The mysterious crash will only happen when the app is archived. That's why I did not notice the cash before and submitted the not working app to iTunes Connect.

I symolicated the crash log and the app crashes at a point where it just doesn't make any sense (for me). Here is the relevant (stripped) code.

I have a class Countries which does not do much more than loading a set of items from Core Data (SQLite):

//
//  Countries.swift
//

import Cocoa

let appCountries = Countries()
class Countries {

    lazy var items: [Country] = {
        if let list = self.load() {
            return list
        }
        else {
            return []
        }
    }()

    func load() -> [Country]? {

        if let countryList = coreDataHelper.fetchEntitiesForClass("Country") as? [Country] {
            return countryList
        }
        else {
            return nil
        }

    }

}

Country is an entity of my data model. The coreDataHelper class simply holds all relevant methods for creating the data store, managed object context and fetching entities. The code is not relevant, works for sure in a dozen of other use cases in the same app. Just assume coreDataHelper.fetchEntitiesForClass() returns all items of an entity.

Now I have a modal window (beginSheet, which I implemented by following this tutorial) which holds a NSPopUpButton and I am populating all the Country elements to this PopUp:

//
//  ModalWindow.swift
//

import Cocoa

class ModalWindow: NSWindowController {

    var mainW: NSWindow = NSWindow()

    let locale = NSLocale.currentLocale()

    @IBOutlet weak var countries: NSPopUpButton!

    override init() {
        super.init()
    }

    override init(window: NSWindow!) {
        super.init(window: window)
        //Initialization code here.
    }

    required init?(coder: (NSCoder!)){
        super.init(coder: coder);
    }

    override func windowDidLoad() {
        super.windowDidLoad()
        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.        
    }

    //method called to display the modal window
    func beginSheet(mainWindow: NSWindow){
        self.mainW = mainWindow
        NSApp.beginSheet(self.window!, modalForWindow: mainWindow, modalDelegate: self, didEndSelector:nil, contextInfo: nil)

        if let countryList = appCountries.items as NSArray? {

            for country in countryList as [Country] {
                if let localized = self.locale.displayNameForKey(NSLocaleCountryCode, value: country.name) {
                    self.countries!.addItemWithTitle(localized)
                }
            }
        }
    }

    //method called to slide out the modal window
    func endSheet(){
        NSApp.endSheet(self.window!)
        self.window!.orderOut(mainW)
    }

}

The symbolicated crash log tells me, the crash did happen in the line where I loop over the countryList:

for country in countryList as [Country] {

Crash-log excerpt:

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       EXC_I386_GPFLT

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libobjc.A.dylib               0x00007fff8b863064 objc_retain + 20
__TFC8MyApp18Modalwindow13windowDidLoadfS0_FT_T_ ModalWindow.swift:35
2   com.apple.AppKit                0x00007fff95dcee07 -[NSWindowController _windowDidLoad] + 586

From what I understand, this is a memory access issue. But I don't understand why. The thing is, when I NSLog the countryList it starts working:

if let countryList = appCountries.items as NSArray? {
  NSLog("[TEST] %@", countryList)
  for country in countryList as [Country] {
    //...
  }
}

I tried to interact in other ways with countryList but everything else will cause a crash when archived, unless I NSLog it first exactly like above. Even this would crash: NSLog("[TEST] %@", "\(countryList)"), even though it should be the exact same from my understanding. And again, this only happens when the application is archived in preparation for sending it to iTunes connect. Zero issues when simply built and run.

I am tempted to simply leave the NSLog in there and ship it that way, but I would like to understand what is going on an how to solve it appropriate.

Edit: Just installed the update Xcode 6.2 and have the same problem as with 6.1.1.


回答1:


I experimented with all the Build Settings which differed between Debug and Release. It looks like this is a Swift compiler issue. The problem disappears when I change the Optimization Level from Fastest to None. Both Fastest and Fastest, Unchecked produce above error. None works.

Now that I have identified the problem, I was able to search and find a better workaround. The problem is somehow related to Arrays in general and I found several mentions of it online. A simple workaround is to cast the array elements to AnyObject, like so:

for country in countryList as [AnyObject] as [Country] {
   //...
}

instead of

for country in countryList as [Country] {
   //...
}

Found this in another SO question: Swift optimization level breaks converting NSArray to Array



来源:https://stackoverflow.com/questions/29003323/memory-access-crash-only-in-archived-cocoa-application-exc-bad-access-exc-i386

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!