2

I'm trying to teach myself Swift via the Stanford iTunes U course (currently working on the calculator app), but I just ran into a super weird issue that I've never seen before and can't figure out how to solve: one of my return statements (and only the return statement itself) is being skipped during runtime.

func evaluateOps(ops: [Op]) -> (result: Double?, remainingOps: [Op]) {

    if !ops.isEmpty {
        var remainingOps = ops
        let op = remainingOps.removeLast()

        switch op {
        case ...
        case ...
        case .BinaryOperation(_, let operation):

            // check that there are 2 operands before it
            if remainingOps.count == 2 {
                let op1Evaluation = evaluateOps(remainingOps)
                if let operand1 = op1Evaluation.result {
                    let op2Evaluation = evaluateOps(op1Evaluation.remainingOps)
                    if let operand2 = op2Evaluation.result {

                        // PROBLEM AREA...
                        let x = (operation(operand1, operand2), op2Evaluation.remainingOps)
                        println("results: \(x.0) remainder: \(x.1)")
                        return (x.0, x.1) // skipped during runtime...
                    }
                }
            }

            else { ... }

        }
    }
    println("returning nil")
    return (nil, ops)
}

// troublesome method is called here...
let (result, remainder) = evaluateOps(opStack)
println("\(opStrings) = \(result) with \(remainderStrings) leftover")

Everything works so that, if I tried to calculate 5*3 for example, the console would read:

results: 15.0 remainder: []
returning nil
[5.0, ×, 3.0] = nil with [5.0, ×, 3.0] leftover

I think the problem might have something to do with the fact that, in the above code, if I tried to simply return x, I get a compile error that reads Cannot express tuple conversion '(Double, [CalculatorModel.Op])' to '(result: Double?, remainingOps: [CalculatorModel.Op])'. I also have no idea what to do with this.

In my researching the problem, I've discovered the downcasting keyword as (see altered code below), which removed the compile error when returning x rather than (x.0, x.1) but results in the same console result (except that now it says results: Optional(15.0) remainder: []):

let x = (operation(operand1, operand2) as Double?, op2Evaluation.remainingOps as [Op])
println("results: \(x.0) remainder: \(x.1)")
return x // no error but same result as return (x.0, x.1)

I've also tried sticking a println(getClassName(x.0!)) just before the return statement to make sure I even had a double, and it spit out __NSCFNumber... which I also researched, and found to be highly underdocumented (or at least not documented well enough to figure out how that's affecting me or how I can fix it!)

Although as you can see, it should be a double...

enum Op {
    case Operand(Double)
    case UnaryOperation(String, Double -> Double)
    case BinaryOperation(String, (Double, Double) -> Double)

    var description: String {
        get {
            switch self {
            case .Operand(let operand):
                return "\(operand)"
            case .UnaryOperation(let symbol, _):
                return "\(symbol)"
            case .BinaryOperation(let symbol, _):
                return "\(symbol)"
            }
        }
    }
}

As you can see, everything's working perfectly fine as it's performing the calculation and everything...until it gets to that little troublesome area. I've searched StackOverflow and did a bit of Googling, but this seems to be a rather uncommon problem... Any help is appreciated! Let me know if you need any further information. Thanks!

EDIT:

getClassName(_:) is defined below (found it online somewhere...):

func getClassName(obj : AnyObject) -> String
{
    let objectClass : AnyClass! = object_getClass(obj)
    let className = objectClass.description()

    return className
}
  • Hard to identify the precise issue amongst the posted code, and there are lots of side issues presented here. I strongly recommend you take it a bit slower and try to understand what each bit of code is doing. I highly doubt the return statement is skipped during runtime, that would be a fairly catastrophic compiler/runtime bug - instead it looks a lot like the recursive nature of the method might be causing confusion. Have you put a breakpoint in there and stepped through line-by-line to see what is happening? – Stuart Jul 28 '15 at 23:20
  • Where is `getClassName(_:)` defined? The returned `__NSCFNumber` implies (at first glance) that it is an Objective-C function - the strange type is an implementation detail that simply indicates that a Swift `Double` is bridged with Foundation's `NSNumber`/`CFNumber` type. As a general rule, if the type is preceded with underscores it relates to a private implementation, which is why you won't find any documentation mentioning it. – Stuart Jul 28 '15 at 23:53
  • 1
    @Stuart you're right about it not technically skipping the return statement. But the problem is still super weird. Using breakpoints, I've discovered that the code is in fact hitting that return statement, but it's failing to return; it still just continues down to the "return nil" area and *actually* returns (nil, ops). Also, I included the implementation of `getClassName(_:)` in the original post, just in case it helps. – theRenaissanceMan Jul 29 '15 at 10:55
  • Yeah so `object_getClass()` is an Objective-C runtime function, which is why it is giving you a description of the bridged type, instead of a Swift `Double`. Rest assured, your number is in fact a `Double`. Regarding the return statement, I am suggesting that when it appears to "fail to return" it is actually exiting one stack frame, and continuing execution of the previous call to the same method. When stepping through your code, pay attention to the stack trace in the debug navigator to the left which will tell you what frame you're currently looking at. – Stuart Jul 29 '15 at 11:27

0 Answers0