2016-07-15 4 views
2

Так что я люблю объявление переменных для хранения возвращаемого значения, а затем возвращает указанную переменную на следующей строке, что упрощает отладку моего кода, я могу просто установить точку останова на линии возврата и посмотреть, какое значение оно возвращает. Я использую это везде, и это делает мой код намного легче отлаживать.Swift: упростить отладку, улавливая возвращаемое значение в заявлении о текущем состоянии

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

Но тогда у вас есть сценарий, где у вас есть и другие УСТРОЙСТВА условия, которые должны быть выполнены для того, чтобы ваш метод к имеет смысл. The guard statement отлично подходит для обеспечения соответствия определенным условиям, а не ввода pyramids of doom.

Но проблема с ранней отдачей заключается в том, что вы получаете как минимум две точки выхода (потому что guard требует в этом контексте 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 бы the defer statement смог поймать возвращаемое значение, не так ли?

Это позволит сделать отладку намного проще, в то же время используя 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 
} 

ответ

1

Мне нравится ваше предложило усовершенствование Swift иметь defer захват возвращаемого значения.

Это то, что будет работать, но это не идеально, потому что для этого требуется небольшая дополнительная работа (и беспорядок кода), но вы можете сделать это вручную, объявив returnValue с let в верхней части своей функции, тот же тип, который возвращает функция. Затем замените все ваши return <something> на returnValue = <something>; return returnValue.

Объявив returnValue с let, Swift сообщит вам, если вы забыли назначить returnValue перед тем, как покинуть эту функцию. Поэтому, если вы добавите новую функцию return в свою функцию, ваша функция не будет компилироваться до тех пор, пока вы не назначите returnValue. Появится сообщение об ошибке: ошибка: константа 'returnValue' используется до инициализации.

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 
} 
+0

Да, именно то, что я обычно делаю! :) Спасибо, что нашли время и предложили это, хотя! :) Я предполагаю, что это единственный способ на данный момент ... – Sajjon

+0

Добавлено второе решение, которое хуже ;-) – vacawama

+0

Ничего себе! Я даже не знал, что можно объявить функцию внутри функции! Это очень творческие решения! – Sajjon