Imprimindo variável opcional

118

Estou tentando com essas linhas de código

class Student {
    var name: String
    var age: Int?

    init(name: String) {
        self.name = name
    }

    func description() -> String {
        return age != nil ? "\(name) is \(age) years old." : "\(name) hides his age."
    }
}

var me = Student(name: "Daniel")
println(me.description())
me.age = 18
println(me.description())

O código acima produz como segue

Daniel hides his age.
Daniel is Optional(18) years old.

Minha dúvida é por que há opcional (18) lá, como posso remover o opcional e apenas impressão

Daniel is 18 years old.
Daniel Adinugroho
fonte

Respostas:

190

Você tem que entender o que realmente é um opcional. Muitos iniciantes do Swift pensam var age: Int?que a idade é um Int que pode ou não ter um valor. Mas significa que a idade é um Opcional que pode ou não conter um Int.

Dentro de sua description()função, você não imprime o Int, mas sim o opcional. Se você quiser imprimir o Int, terá que desembrulhar o Opcional. Você pode usar "ligação opcional" para desembrulhar um Opcional:

if let a = age {
 // a is an Int
}

Se tiver certeza de que o Opcional contém um objeto, você pode usar o "desembrulhamento forçado":

let a = age!

Ou em seu exemplo, como você já tem um teste para nil na função de descrição, pode apenas alterá-lo para:

func description() -> String {
    return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}
Apfelsaft
fonte
Acho que o exemplo seria um pouco melhor se você alterasse para usarif let age = age { return ""} else { return "" }
kubi
13
+1 para:Many Swift beginners think var age: Int? means that age is an Int which may or may not have a value. But it means that age is an Optional which may or may not hold an Int.
Lee
18

Um opcional significa que o Swift não tem certeza se o valor corresponde ao tipo: por exemplo, Int? significa que Swift não tem certeza se o número é um Int.

Para removê-lo, existem três métodos que você pode empregar.

1) Se você tiver certeza absoluta do tipo, pode usar um ponto de exclamação para forçar o desembrulhamento, assim:

// Here is an optional variable:

var age: Int?

// Here is how you would force unwrap it:

var unwrappedAge = age!

Se você forçar o desempacotamento de um opcional e ele for igual a nulo, poderá encontrar este erro de falha:

insira a descrição da imagem aqui

Isso não é necessariamente seguro, então aqui está um método que pode evitar travamento caso você não tenha certeza do tipo e valor:

Os métodos 2 e três protegem contra esse problema.

2) O Opcional Implicitamente Não Embalado

 if let unwrappedAge = age {

 // continue in here

 }

Observe que o tipo não embalado agora é Int , em vez de Int? .

3) A declaração de guarda

 guard let unwrappedAge = age else { 
   // continue in here
 }

A partir daqui, você pode seguir em frente e usar a variável desembrulhada. Certifique-se apenas de forçar o desempacotamento (com um!), Se tiver certeza do tipo da variável.

Boa sorte com seu projeto!

GJZ
fonte
1
usando idade de retorno! = nulo? "(nome) tem (idade!) anos." : "(nome) esconde sua idade."
leedream
1
Me salvou de uma dor de cabeça com um problema que eu tive. Não posso agradecer o suficiente.
Bruno Recillas
8

Para fins de teste / depuração, geralmente desejo gerar opcionais como strings sem sempre ter que testar os nilvalores, então criei um operador personalizado.

Melhorei ainda mais as coisas depois de ler essa resposta em outra pergunta .

fileprivate protocol _Optional {
    func unwrappedString() -> String
}

extension Optional: _Optional {
    fileprivate func unwrappedString() -> String {
        switch self {
        case .some(let wrapped as _Optional): return wrapped.unwrappedString()
        case .some(let wrapped): return String(describing: wrapped)
        case .none: return String(describing: self)
        }
    }
}

postfix operator ~? { }
public postfix func ~? <X> (x: X?) -> String {
    return x.unwrappedString
}

Obviamente, o operador (e seus atributos) pode ser ajustado a seu gosto ou você pode torná-lo uma função. De qualquer forma, isso permite que você escreva um código simples como este:

var d: Double? = 12.34
print(d)     // Optional(12.34)
print(d~?)   // 12.34
d = nil
print(d~?)   // nil

Integrar a ideia de protocolo do outro fez com que funcionasse até mesmo com opcionais aninhados, que costumam ocorrer ao usar o encadeamento opcional. Por exemplo:

let i: Int??? = 5
print(i)              // Optional(Optional(Optional(5)))
print("i: \(i~?)")    // i: 5
Jeremy
fonte
1
A partir do Swift 3, você também pode usar a função debugPrint da biblioteca padrão do Swift (_: separator: terminator :). No entanto, isso imprimirá a string entre aspas duplas.
psmythirl
@psmythirl: Bom saber! No entanto, às vezes você pode querer simplesmente passar ou incorporar um opcional como um Stringoutro lugar (por exemplo, um log / mensagem de erro).
Jeremy
imprimir (d como Qualquer)
DawnSong
7

Atualizar

Basta usar me.age ?? "Unknown age!". Funciona no 3.0.2.

Resposta Antiga

Sem forçar o desempacotamento (sem sinal mach / travamento se nulo), outra boa maneira de fazer isso seria:

(result["ip"] ?? "unavailable").description.

result["ip"] ?? "unavailable" deveria ter trabalho também, mas não tem, não no 2.2 pelo menos

Claro, substitua "indisponível" pelo que mais lhe convier: "nulo", "não encontrado" etc.

Valentin Radu
fonte
4

Para desembrulhar use opcional em age!vez de age. Atualmente você está imprimindo um valor opcional que poderia ser nil. É por isso que está embrulhado Optional.

Kirsteins
fonte
4

Em swift Optionalé algo que pode ser nilem alguns casos. Se você tiver 100% de certeza que a variableterá algum valor sempre e não retornará nilo add! com a variável para forçar o desempacotamento.

Em outro caso, se você não tiver muita certeza sobre o valor, adicione um if letbloco ou guardpara certificar-se de que o valor existe, caso contrário, pode resultar em um travamento.

Para if letbloco:

if let abc = any_variable {
 // do anything you want with 'abc' variable no need to force unwrap now.
}

Para guarddeclaração:

guard é uma estrutura condicional para retornar o controle se a condição não for atendida.

Eu prefiro usar guardover if letblock em muitas situações, pois nos permite retornar o functionse um determinado valor não existir. Como quando há uma função em que uma variável é integral para existir, podemos verificar se há uma instrução em guarda e o retorno dela não existe. ie;

guard let abc = any_variable else { return }

Se a variável existe, podemos usar 'abc' na função fora do escopo de guarda.

Muhammad Yawar Ali
fonte
3

ageé um tipo opcional: Optional<Int>então, se você comparar com nil, ele retornará false sempre que tiver um valor ou não. Você precisa desembrulhar o opcional para obter o valor.

No seu exemplo, você não sabe se ele contém algum valor, então você pode usar isso:

if let myAge = age {
    // there is a value and it's currently undraped and is stored in a constant
}
else {
   // no value
}
Greg
fonte
3

Fiz isso para imprimir o valor da string (propriedade) de outro controlador de exibição.

ViewController.swift

var testString:NSString = "I am iOS Developer"

SecondViewController.swift

var obj:ViewController? = ViewController(nibName: "ViewController", bundle: nil)
print("The Value of String is \(obj!.testString)")

Resultado:

The Value of String is I am iOS Developer
iCodeAtApple
fonte
2
Você não deve forçar o desembrulhar um opcional
E-Riddie
3

Confira o guarddepoimento:

for student in class {
    guard let age = student.age else { 
        continue 
     }
    // do something with age
}
Phil Hudson
fonte
3

Ao ter um valor padrão:

print("\(name) is \(age ?? 0) years old")

ou quando o nome é opcional:

print("\(name ?? "unknown") is \(age) years old")

arnon
fonte
1

Eu estava recebendo o Optional ("String") em minhas células tableview.

A primeira resposta é ótima. E me ajudou a descobrir. Aqui está o que eu fiz, para ajudar os novatos como eu.

Como estou criando um array em meu objeto personalizado, sei que ele sempre terá itens na primeira posição, então posso forçar o desdobramento em outra variável. Em seguida, use essa variável para imprimir ou, no meu caso, defina como o texto da célula tableview.

let description = workout.listOfStrings.first!
cell.textLabel?.text = description

Parece tão simples agora, mas demorei um pouco para descobrir.

Joshua Dance
fonte
-1

Esta não é a resposta exata para essa pergunta, mas uma das razões para esse tipo de problema. No meu caso, não fui capaz de remover Optional de uma String com "if let" e "guard let".

Portanto, use AnyObject em vez de Any para remover o opcional de uma string rapidamente.

Por favor, consulte o link para a resposta.

https://stackoverflow.com/a/51356716/8334818

Pramod More
fonte