ios - 幻象 EXC_BAD_ACCESS

标签 ios iphone swift xcode memory

我之前遇到的所有其他 BAD_ACCESS 通常都是一个快速的拼写错误修复,但这一个非常令人困惑

此代码应该下载 .ics 谷歌日历文件,然后将其传递给解析器函数,该函数将返回 Event 对象数组(此处未包含)

问题是,在物理 iOS 设备上测试时,每第 3-4 次运行 loadEventIntoCalendar(),调用此行时都会抛出 EXC_BAD_ACCESS

tempHold = calendarString.substring(with: tempRange)

在 event() 函数的 while 循环中。

我尝试使用许多不同的技术来解决这个问题。 僵尸对象直接不会在日志中打印任何内容。 我尝试使用Instruments分析运行,但没有发现任何有用的东西。我尝试制作参数中传递的字符串文件的单独副本,但这没有改变任何内容。

我确信这个问题与calendarString有关,或者至少与它指向的值有关。我尝试通过 Xcode 分析内存块,但找不到任何变量,这些变量会指向导致错误的内存位置。

我非常确定 RAM 不会过载,因为整个应用程序最多仅占用约 70 MB(使用 Instruments 检查)

events() 应该是一个单例静态函数。

这是两个函数

func loadEventsIntoCalendar() {
    // The link from which the calendar is downloaded
    let url = URL (string: "https://calendar.google.com/calendar/ical/wlmacci%40gmail.com/public/basic.ics")!


    // The process of downloading and parsing the calendar
    let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
        // The following is simply a declaration and will not execute without the line 'task.resume()'
        if let URLContent = data {  // If Data has been loaded
            // If you got to this point then you've downloaded the calendar so...
            // Calendar File parsing starts here!!!
            // The string that holds the contents of the calendar's events
            var webContent:String = String(data: URLContent, encoding:String.Encoding.utf8)!
            self.events(forCalendarFile: &webContent, inCalendar: Calendar(identifier: Calendar.Identifier.gregorian))
        }
    })
    task.resume()
}

// Calendar Parser for this VC
func events(forCalendarFile webContent:inout String, inCalendar calendar:Calendar) {
    // The reason for this complication is, i thought copying the object might solve the issue, it did not :(
    let NSWebContent = NSString(string: webContent)
    let calendarString = NSWebContent.copy() as! NSString

    // An array of flags used for locating the event fields
    // [h][0] - The flag that marks the begining of a field, [h][1] - The flag that marks the end of a field
    let searchTitles:[[String]] = [["SUMMARY:", "TRANSP:"], ["DESCRIPTION:", "LAST-MODIFIED:"], ["DTSTART", "DTEND"], ["DTEND", "DTSTAMP"], ["LOCATION:", "SEQUENCE:"]]

    // The range of "webContent's" content that is to be scanned
    // Must be decreased after each event is scanned
    var range:NSRange = NSMakeRange(0, calendarString.length - 1)
    // Inside function that will be used to determine the 'difference' range between the begining and end flag ranges.
    let findDifference:(NSRange, NSRange) -> NSRange = {(first:NSRange, second:NSRange) -> NSRange in
        let location = first.location + first.length, length = second.location - location   // Determine the start position and length of our new range
        return NSMakeRange(location, length)                                                // Create and return the new range
    }
    // Inside function that will be used to move the searching range to the next event
    // Returns an NSNotFound range (NSNotFound, 0) if there are no more events
    let updateRange:(NSRange) -> NSRange = {(oldRange:NSRange) -> NSRange in
        let beginingDeclaration = calendarString.range(of: "BEGIN:VEVENT", options: NSString.CompareOptions.literal, range: oldRange)
        // If the "BEGIN:VEVENT" was not found in webContent (no more events)
        if NSEqualRanges(beginingDeclaration, NSMakeRange(NSNotFound, 0)) {
            return beginingDeclaration  // Return an 'NSNotFound' range (Named it myself;)
        }
        // Calculate the index of the last character of 'beginingDeclaration' flag
        let endOfBeginingDeclaration = beginingDeclaration.location + beginingDeclaration.length
        // Calculate the length of the new range
        let length = oldRange.length - endOfBeginingDeclaration + oldRange.location
        // Calculate the starting location of the new range
        let location = endOfBeginingDeclaration
        // Create and return the new range
        return NSMakeRange(location, length)
    }

    // A holder for the begining and end flags for each event field
    var fieldBoundaries:[NSRange]
    // The actual parsing of each event
    repeat {
        range = updateRange(range)  // Move our searching range to the next event
        if NSEqualRanges(range, NSMakeRange(NSNotFound, 0)) {   // If there are no more events in the searching range
            break;                                              // Then no more shall be added (break from the loop)
        }

        var tempHold:String!
        // Record each field into our event database
        for h in 0...searchTitles.count-1 {
            fieldBoundaries = [NSRange]()   // Clear the fieldBoundaries for the new search
            fieldBoundaries.append(calendarString.range(of: searchTitles[h][0], options: NSString.CompareOptions.literal, range: range))   // Find the begining flag
            fieldBoundaries.append(calendarString.range(of: searchTitles[h][1], options: NSString.CompareOptions.literal, range: range))   // Find the ending flag
            let tempRange = findDifference(fieldBoundaries[0], fieldBoundaries[1])
            print ("Isolating event content")
            tempHold = calendarString.substring(with: tempRange)                         // Create a new string from whatever is in between the two flags. This will be the current field of the event
            print ("Event content isolated")
            tempHold = tempHold.trimmingCharacters(in: CharacterSet.newlines)                                           // Remove all /r /n and other 'new line' characters from the event field
            tempHold = tempHold.replacingOccurrences(of: "\u{005C}", with: "", options: .literal, range: nil)           // Replace all backslashes from the event field
        }
    } while (true)
}

这必须相对较快地完成,因此我们将不胜感激。

提前致谢!

最佳答案

据我所知,字符串搜索方法不能保证结果范围的长度为0。如果替换这两个范围检查会发生什么?

if range.location == NSNotFound { ... }

而不是

if NSEqualRanges(range, NSMakeRange(NSNotFound, 0)) { ... }

关于ios - 幻象 EXC_BAD_ACCESS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40814961/

相关文章:

iphone - 为 UITableView 存储来自网络的图像

ios - 收到广告时向下滑动 AdView

ios - 更改 ScrollView 中的按钮位置

ios - time.h函数看不到头文件

ios - 第一次启动/segue swift

swift - 关联类型的协议(protocol)继承

swift - 如何快速将数据设置为同一类中的第二个 TableView

objective-c - 为 NSNotifications 注册所有 View Controller

iphone - 如何在 iPhone 中格式化 SQLite 日期?

iphone - 删除核心数据中实体中的所有实例的最有效方法是什么?