Swift字符串操作导致EXC_BAD_ACCESS

时间:2014-06-13 15:09:07

标签: ios string crash swift exc-bad-access

我正在使用时间计算器来尝试学习Swift,而且我遇到了格式化用户输入导致访问错误的问题。

以下是导致崩溃的代码(最初包含在循环中但是为了找到问题而分解):

var outputString : String = ""
let prefixString : String = "00"

let combinedString1 : String = prefixString + self.timeIntervalInput[0]
let combinedString1a : String = combinedString1.substringFromIndex(countElements(combinedString1) - 2)
outputString += combinedString1a

let combinedString2 : String = prefixString + self.timeIntervalInput[1]
let combinedString2a : String = combinedString2.substringFromIndex(countElements(combinedString2) - 2)
outputString += combinedString2a    // CRASH USUALLY HAPPENS HERE

let combinedString3 : String = prefixString + self.timeIntervalInput[2]
let combinedString3a : String = combinedString3.substringFromIndex(countElements(combinedString3) - 2)
outputString += combinedString3a

用户的输入存储在字符串数组中(小时/分钟/秒/毫秒)。上面的代码应该用0填充间隙(因此2变为02,空字符串变为00等),并且每次更新用户的输入时都会运行。

输入第一个或第二个字符后,上面的代码崩溃了。将prefixString从let更改为var会使其在稍长时间内正常工作,因此它会在第三个字符后崩溃。

我在这里做错了什么,或者这是Swift中的错误?

修改(附加信息):

这是整个结构:

struct TimeCalcInput
{
    var timeIntervalInput : String[] = ["", "", "", ""]
    var timeIntervalIndex : Int = 0
    var decimalInput : String = ""
    var timeType : TimeTypeEnum = TimeTypeEnum.AM
    var potentialInput : String = ""

    var label : String {
        get {

            if (self.timeType == TimeTypeEnum.Decimal) {
                return self.decimalInput
            } else {
//              var outputString : String = ""
//              for (var i : Int = 0; i < 3; i++) {
//                  let prefixString : String = "00"
//                  var combinedString : String = ""
//                  combinedString += prefixString + self.timeIntervalInput[i]
//                  combinedString = combinedString.substringFromIndex(countElements(combinedString) - 2)
//                  outputString = outputString + "\(combinedString)"
//              }
                var outputString : String = ""
                let prefixString : String = "00"

                let combinedString1 : String = prefixString + self.timeIntervalInput[0]
                let combinedString1a : String = combinedString1.substringFromIndex(countElements(combinedString1) - 2)
                outputString += combinedString1a

                let combinedString2 : String = prefixString + self.timeIntervalInput[1]
                let combinedString2a : String = combinedString2.substringFromIndex(countElements(combinedString2) - 2)
                outputString += combinedString2a    // CRASH USUALLY HAPPENS HERE

                let combinedString3 : String = prefixString + self.timeIntervalInput[2]
                let combinedString3a : String = combinedString3.substringFromIndex(countElements(combinedString3) - 2)
                outputString += combinedString3a

                return ""
                let test : String = "000"
                var asd : String = test + self.timeIntervalInput[3]
                asd = asd.substringFromIndex(countElements(asd) - 3)
                outputString += asd

                if (self.timeType == TimeTypeEnum.AM) {
                    outputString += " AM"
                } else if (self.timeType == TimeTypeEnum.PM) {
                    outputString += " PM"
                }

                println(outputString)

                return outputString
            }
        }
    }


    mutating func validateNewInput(inputString : String) -> Bool
    {
        switch (self.timeType)
            {
        case TimeTypeEnum.Decimal:
            return self.validateNewInputForDecimal(inputString)
        case TimeTypeEnum.Interval:
            //                  return self.validateNewInputForInterval(inputString)
            return false
        case TimeTypeEnum.AM, TimeTypeEnum.PM:
            var temp : Bool = self.validateNewInputForRawTime(inputString, isMilitaryTime:false)
            println(self.timeIntervalInput)
            return temp
        case TimeTypeEnum.Military:
            var temp : Bool =  self.validateNewInputForRawTime(inputString, isMilitaryTime:true)
            println(self.timeIntervalInput)
            return temp
        default:
            return false
        }
    }


    mutating func validateNewInputForDecimal(inputString : String) -> Bool
    {
        let combinedString : String = self.decimalInput + inputString

        // note: this regex fails on letters-only strings.  not an issue now but something to watch
        let predicate : NSPredicate = NSPredicate(format: "SELF MATCHES '^[0-9]*(\\.[0-9]*)'")
        let stringTest : Bool = predicate.evaluateWithObject(combinedString)

        if (stringTest) {
            self.decimalInput = combinedString
            return true
        }
        return false
    }

    // used to evaluate an incoming string when the string is assumed to represent a raw time (e.g. 5:30am)
    // since the string is being built one character at a time, each character needs to be evaluated separately
    // to ensure that the final time is valid
    mutating func validateNewInputForRawTime(inputString : String, isMilitaryTime : Bool) -> Bool
    {
        let currentString = self.timeIntervalInput[self.timeIntervalIndex]

        // if the incoming value is a ":" assume the user wants to move on to the next section
        // (e.g. hours --> minutes).  Fill in any gaps in the current section, then advance to the next section
        if (inputString == ":") {
            // check to make sure there is a "next" section
            if (self.timeIntervalIndex < self.timeIntervalInput.count - 1)
            {
                // if the current section is incomplete, fill any gaps with 0s
                let prefixString = "00"
                var combinedString = prefixString + currentString
                combinedString = combinedString.substringFromIndex(countElements(combinedString) - 2)
                self.timeIntervalInput[self.timeIntervalIndex] = combinedString

                // go to the next section
                self.timeIntervalIndex++
                return true
            }
            // if the incoming value is a number, evaluate it to make sure it's valid, and if so, add it
        } else {

            let combinedString = currentString + inputString

            // each section (hours, minutes, etc, has its own rules for whether the input is valid
            switch (self.timeIntervalIndex)
            {
                // hours
                case 0:
                    // if empty, accept any number
                    if (countElements(currentString) == 0) {
                        self.timeIntervalInput[self.timeIntervalIndex] = combinedString
                        return true
                        // if one digit exists, make sure total value is less than 12/24 (format depending)
                    } else if (countElements(currentString) == 1) {
                        if ((combinedString.toInt() < 24 && isMilitaryTime) || (combinedString.toInt() < 12)) {
                            self.timeIntervalInput[self.timeIntervalIndex] = combinedString
                            return true
                        }
                        // if both digits exist, skip to the next section and add it
                    } else {
                        self.timeIntervalIndex++
                        self.timeIntervalInput[self.timeIntervalIndex] = inputString
                        return true
                    }
                // minutes, seconds
                case 1, 2:
                    // if empty, accept any number
                    if (countElements(currentString) == 0) {
                        self.timeIntervalInput[self.timeIntervalIndex] = combinedString
                        return true
                        // if one digit exists, make sure total value is less than 60
                    } else if (countElements(currentString) == 1) {
                        if (combinedString.toInt() < 60) {
                            let combinedString = currentString + inputString
                            self.timeIntervalInput[self.timeIntervalIndex] = combinedString
                            return true
                        }
                        // if both digits exist, skip to the next section and add it
                    } else {
                        self.timeIntervalIndex++
                        self.timeIntervalInput[self.timeIntervalIndex] = inputString
                        return true
                    }
                // milliseconds
                case 3:
                    // accept any combined total less than 1000
                    if (combinedString.toInt() < 1000) {
                        let combinedString = currentString + inputString
                        self.timeIntervalInput[self.timeIntervalIndex] = combinedString
                        return true
                    }
                default:
                    break
            }
        }
        return false
    }
}

在这里使用它(输入是上述结构的数组):

// -----------------------------------------------------------------------------------------------------------------
    @IBAction func addInputCharacter(#sender : UIButton)
    {
        let inputChar : String = String(sender.tag)
        self.inputs[self.currentInput].validateNewInput(inputChar)
        self.updateLabelsAfterInput()
    }

    // -----------------------------------------------------------------------------------------------------------------
    func updateLabelsAfterInput()
    {
        self.lblMainLabel.text = self.inputs[self.currentInput].label
        if (self.currentInput == 0) {
            self.lblSecondaryLabel.text = ""
        } else {
            self.lblSecondaryLabel.text = self.inputs[0].label
        }
    }

用户首次输入号码时会发生崩溃。我在没有更新标签的情况下测试了它,并且timeIntervalInputs数组完全设置好了(输入&#34; 1&#34;后,数组读取[1 ,,,],然后输入0变为[10, ,,],)

崩溃时的错误消息是

  

线程1:EXC_BAD_ACCESS(代码= 1,地址= 0x12aff2cf0)

在崩溃之前,

combinedString2a正在存储正确的值(&#34; 00&#34;)。崩溃后,Quick Look表示值为&#34;(无)&#34;并打印描述给出以下消息:

  

(String)combinedString2a = DW_OP_piece for offset 8:stack of top is   不是一块

2 个答案:

答案 0 :(得分:1)

我一直在遇到同样的错误,我只能认为它是Swift中的一个错误。这是一个非常简单的例子。 txtName是文本字段的IBOutlet,用户在其中键入其名称,labResult是放置结果的标签。在下面的代码中,一旦我们尝试调用.lowercaseString方法,我们每次都会在modifyString中遇到错误。 (我对Swift和iOS编程都很新,所以我可能会犯一些愚蠢的错误,但我不知道是什么。)

@IBAction func butDoIt_Clicked(sender : UIButton) {
    var myName = "Fred Bloggs"
    labResult.text = modifyString(myName) //this works just fine

    if txtName.text
    {
        myName = txtName.text!
        labResult.text = modifyString(myName) //this crashes with EXC-BAD-ACCESS
    }
   }

func modifyString(aName:String) ->String
{
    let lowername = aName.lowercaseString
    let uppername = aName.uppercaseString
    return lowername + "-" + uppername
}

答案 1 :(得分:1)

我已经向Apple提交了一份错误报告。与此同时,我通过用NSStrings替换组合字符串并使用方法的NSString版本找到了一种解决方法。

var outputString : String = ""
let prefixString : String = "00"

let combinedString1 : NSString = prefixString.stringByAppendingString(self.timeIntervalInput[0])
let combinedString1a : NSString = combinedString1.substringFromIndex(combinedString1.length - 2)
outputString += combinedString1a

let combinedString2 : NSString = prefixString.stringByAppendingString(self.timeIntervalInput[1])
let combinedString2a : NSString = combinedString2.substringFromIndex(combinedString2.length - 2)
outputString += combinedString2a

let combinedString3 : NSString = prefixString.stringByAppendingString(self.timeIntervalInput[2])
let combinedString3a : NSString = combinedString3.substringFromIndex(combinedString3.length - 2)
outputString += combinedString3a

return outputString