我为 OS X 创建了第一个 Cocoa 应用程序,并很高兴最终将其提交到 iTunes connect。只是因为应用程序崩溃而未获得批准。我很难弄清楚所有事情,直到这里,现在我终于陷入困境,不知道如何解决以下问题:
首先,该应用程序在简单构建和运行时工作得非常好。仅当应用程序存档时才会发生神秘的崩溃。这就是为什么我之前没有注意到现金并将无法运行的应用程序提交到 iTunes Connect 的原因。
我符号化了崩溃日志,应用程序崩溃了(对我来说)没有任何意义。这是相关的(剥离的)代码。
我有一个类Countries
,它除了从核心数据(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
是我的数据模型的实体。 coreDataHelper 类仅包含用于创建数据存储、托管对象上下文和获取实体的所有相关方法。该代码不相关,但在同一应用程序的许多其他用例中肯定可以工作。假设 coreDataHelper.fetchEntitiesForClass() 返回实体的所有项目。
现在我有一个模态窗口(beginSheet
,我通过以下 this tutorial 实现),它包含一个 NSPopUpButton
并且我正在填充所有 Country
元素添加到此 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)
}
}
符号化的崩溃日志告诉我,崩溃确实发生在我循环 countryList
的行中:
for country in countryList as [Country] {
崩溃日志摘录:
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
据我了解,这是一个内存访问问题。但我不明白为什么。问题是,当我 NSLog
countryList
它开始工作:
if let countryList = appCountries.items as NSArray? {
NSLog("[TEST] %@", countryList)
for country in countryList as [Country] {
//...
}
}
我尝试以其他方式与countryList
进行交互,但其他一切都会在存档时导致崩溃,除非我首先像上面那样NSLog
它。即使这样也会崩溃:NSLog("[TEST] %@", "\(countryList)")
,尽管根据我的理解它应该是完全相同的。再次强调,只有当应用程序被存档以准备将其发送到 iTunes connect 时,才会发生这种情况。简单构建和运行时零问题。
我很想简单地将 NSLog
留在那里并以这种方式发送,但我想了解发生了什么以及如何适本地解决它。
编辑: 刚刚安装了更新 Xcode 6.2,并且遇到了与 6.1.1 相同的问题。
最佳答案
我尝试了调试
和发布
之间不同的所有build设置。看起来这是一个 Swift 编译器问题。当我将优化级别
从最快
更改为无
时,问题就消失了。 Fastest
和 Fastest, Unchecked
都会产生上述错误。 没有
有效。
现在我已经确定了问题,我可以搜索并找到更好的解决方法。这个问题在某种程度上与一般的数组有关,我在网上发现了一些关于它的提及。一个简单的解决方法是将数组元素转换为 AnyObject
,如下所示:
for country in countryList as [AnyObject] as [Country] {
//...
}
而不是
for country in countryList as [Country] {
//...
}
在另一个问题中找到了这个:Swift optimization level breaks converting NSArray to Array
关于xcode - 仅在存档的 Cocoa 应用程序中内存访问崩溃(EXC_BAD_ACCESS/EXC_I386_GPFLT),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29003323/