3

所以我喜欢声明变量来保存返回值,然后在下一行返回所述变量,从而使调试我的代码变得容易,我可以在返回行设置一个断点并查看它返回的值。我在任何地方都使用它,它使我的所有代码都更容易调试。

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let cellCount = models.count
    return cellCount
}

但是,您会遇到这样一种情况,即您必须满足选项和不同的条件才能使您的方法有意义。守卫声明非常适合确保满足某些条件,同时不会引入末日金字塔

但是提前返回的问题是您从您的方法中至少获得了两个退出点(因为在这种情况下guard需要 a return),这使得调试变得更加困难。

// Instantiate using dependency injection
private let reachability: ReachabilityProtocol
private let apiClient: APIClientProtocol

    // Returns true if could start login request, else false 
    func loginUser(username: String, password: String) -> Bool {
        defer {
             // Not possible, would be nice! Return value would be an implicitly declared variable
             // exaclty like the variables 'newValue' and 'oldValue' in property observers!
            print("return value: \(returnValue)")
        }
        guard reachability.isOnline && !username.isEmpty && !password.isEmpty { return false }
        apiClient.loginUser(username, password: password)
        return true
    }

如果 Swift 3.X 能让defer 语句能够捕获返回值,那该多好,不是吗?

这将使调试变得更加容易,同时仍然使用guard和早期返回。我不知道在编写编译器等方面有什么意义,但感觉在即将到来的 Swift 版本中实现这一点并不

你能想出一种不同的方法来实现单点读取具有多个退出点的方法的返回值吗?(无需等待我建议的改进defer?)

编辑
我上面的登录示例不是一个完美的示例,对不起,我为什么要编写这样的代码?哈哈!但是还有很多其他类似的场景,可能是这样的,使用do-try-catch也会使代码难以调试:

// We don't know the return value of this function! Makes it hard to debug!
func fetchUserByFirstName(firstName: String, andLastName lastName: String, fromContext context: NSManagedObjectContext) -> User? {
    defer {
         // Not possible, would be nice! Return value would be an implicitly declared variable
         // exaclty like the variables 'newValue' and 'oldValue' in property observers!
        print("return value: \(returnValue)")
    }

    guard !firstName.isEmpty else { print("firstName can't be empty"); return nil }
    guard !lastName.isEmpty else { print("lastName can't be empty"); return nil }
    // Imagine we have some kind of debug user... Does not really make sense, but good for making a point.
    guard firstName != "DEBUG" else { return User.debugUser }
    let fetchRequest = NSFetchRequest(entityName: Users.entityName)
    let predicate = NSPredicate(format: "firstName == \(firstName) AND lastName == \(lastName)")
    fetchRequest.predicate = predicate
    do {
        let user = try context.executeFetchRequest(fetchRequest)
        return user
    } catch let error as NSError {
        print("Error fetching user: \(error)")
    }
    return nil
}
4

1 回答 1

1

我喜欢你建议的对 Swift 的改进以defer捕获返回值。

这是一些可行的方法,但它并不完美,因为它需要一些额外的工作(和代码混乱),但您可以通过在函数顶部声明returnValuewith手动完成,赋予它与函数相同的类型let返回。然后,将您的所有内容替换return <something>returnValue = <something>; return returnValue.

通过声明returnValuewith ,如果您在离开函数之前let忘记分配,Swift 会通知您。returnValue因此,如果您return向函数添加新的,则在分配returnValue. 你会看到错误:error: constant 'returnValue' used before being initialized

func fetchUserByFirstName(firstName: String, andLastName lastName: String, fromContext context: NSManagedObjectContext) -> User? {
    let returnValue: User?
    defer {
        print("return value: \(returnValue)")
    }

    guard !firstName.isEmpty else { print("firstName can't be empty"); returnValue = nil; return returnValue }
    guard !lastName.isEmpty else { print("lastName can't be empty"); returnValue = nil; return returnValue }
    // Imagine we have some kind of debug user... Does not really make sense, but good for making a point.
    guard firstName != "DEBUG" else { returnValue = User.debugUser; return returnValue }
    let fetchRequest = NSFetchRequest(entityName: Users.entityName)
    let predicate = NSPredicate(format: "firstName == \(firstName) AND lastName == \(lastName)")
    fetchRequest.predicate = predicate
    do {
        let user = try context.executeFetchRequest(fetchRequest)
        returnValue = user; return returnValue
    } catch let error as NSError {
        print("Error fetching user: \(error)")
    }
    returnValue = nil; return returnValue
}

或者(只是在这里集思广益......),将具有多个退出点的函数放入内部函数中,然后调用它:

func fetchUserByFirstName(firstName: String, andLastName lastName: String, fromContext context: NSManagedObjectContext) -> User? {

    func innerFunc(firstName: String, andLastName lastName: String, fromContext context: NSManagedObjectContext) -> User? {

        guard !firstName.isEmpty else { print("firstName can't be empty"); return nil }
        guard !lastName.isEmpty else { print("lastName can't be empty"); return nil }
        // Imagine we have some kind of debug user... Does not really make sense, but good for making a point.
        guard firstName != "DEBUG" else { return User.debugUser }
        let fetchRequest = NSFetchRequest(entityName: Users.entityName)
        let predicate = NSPredicate(format: "firstName == \(firstName) AND lastName == \(lastName)")
        fetchRequest.predicate = predicate
        do {
            let user = try context.executeFetchRequest(fetchRequest)
            return user.first as? User
        } catch let error as NSError {
            print("Error fetching user: \(error)")
        }
        return nil
    }

    let returnValue = innerFunc(firstName, andLastName: lastName, fromContext: context)
    print("return value: \(returnValue)")
    return returnValue
}
于 2016-07-15T11:52:12.957 回答