Antes que eu possa descrever os casos de uso dos Opcionais Implicitamente Desembrulhados, você já deve entender o que são os Opcionais e os Opcionais Implicitamente Desembrulhados no Swift. Caso contrário, recomendo que você leia primeiro meu artigo sobre opcionais
Quando usar um opcional não implicitamente desembrulhado
Há duas razões principais para criar um Opcional Implicitamente Desembrulhado. Tudo tem a ver com a definição de uma variável que nunca será acessada quando nil
, caso contrário, o compilador Swift sempre o forçará a desembrulhar explicitamente um opcional.
1. Uma constante que não pode ser definida durante a inicialização
Cada constante de membro deve ter um valor no momento em que a inicialização é concluída. Às vezes, uma constante não pode ser inicializada com seu valor correto durante a inicialização, mas ainda é possível garantir um valor antes de ser acessada.
O uso de uma variável Opcional contorna esse problema porque um Opcional é inicializado automaticamente nil
e o valor que eventualmente conterá ainda será imutável. No entanto, pode ser uma tarefa difícil desembrulhar constantemente uma variável que você sabe com certeza não é nula. Opcionais implicitamente desembrulhados alcançam os mesmos benefícios que um opcional com o benefício adicional de que não é necessário desembrulhá-lo explicitamente em todos os lugares.
Um ótimo exemplo disso é quando uma variável de membro não pode ser inicializada em uma subclasse UIView até que a visualização seja carregada:
class MyView: UIView {
@IBOutlet var button: UIButton!
var buttonOriginalWidth: CGFloat!
override func awakeFromNib() {
self.buttonOriginalWidth = self.button.frame.size.width
}
}
Aqui, você não pode calcular a largura original do botão até que a exibição seja carregada, mas você sabe que awakeFromNib
isso será chamado antes de qualquer outro método na exibição (que não seja a inicialização). Em vez de forçar o valor a ser explicitamente desembrulhado sem sentido em toda a sua classe, você pode declará-lo como um Opcional Implicitamente Desembrulhado.
2. Quando seu aplicativo não pode se recuperar de um ser variável nil
Isso deve ser extremamente raro, mas se o aplicativo não puder continuar em execução se uma variável for nil
acessada, seria um desperdício de tempo se preocupar em testá-lo nil
. Normalmente, se você tem uma condição que deve ser absolutamente verdadeira para que seu aplicativo continue em execução, use um assert
. Um opcional não implicitamente desembrulhado possui uma afirmação de nada embutida nele. Mesmo assim, geralmente é bom desembrulhar o opcional e usar uma afirmação mais descritiva, se for nulo.
Quando não usar um opcional implicitamente desembrulhado
1. Variáveis de membro calculadas preguiçosamente
Às vezes, você tem uma variável de membro que nunca deve ser nula, mas não pode ser definida com o valor correto durante a inicialização. Uma solução é usar um opcional implicitamente desembrulhado, mas a melhor maneira é usar uma variável lenta:
class FileSystemItem {
}
class Directory : FileSystemItem {
lazy var contents : [FileSystemItem] = {
var loadedContents = [FileSystemItem]()
// load contents and append to loadedContents
return loadedContents
}()
}
Agora, a variável de membro contents
não é inicializada até a primeira vez que é acessada. Isso dá à classe a chance de entrar no estado correto antes de calcular o valor inicial.
Nota: Isso pode parecer contradizer o número 1 acima. No entanto, há uma distinção importante a ser feita. O buttonOriginalWidth
acima deve ser definido durante o viewDidLoad para impedir que alguém altere a largura dos botões antes que a propriedade seja acessada.
2. Em todo lugar
Na maioria das vezes, os Opcionais Implicitamente Desembrulhados devem ser evitados porque, se usado incorretamente, todo o seu aplicativo falhará quando for acessado enquanto nil
. Se você nunca tiver certeza se uma variável pode ser nula, sempre use o Opcional normal. Desembrulhar uma variável que nunca é nil
certamente não dói muito.
if someOptional
.hasValue
está definido no Optional. Eu prefiro a semântica do quehasValue
a de!= nil
. Eu sinto que é muito mais compreensível para novos programadores que não usaramnil
em outros idiomas.hasValue
é muito mais lógico quenil
.hasValue
foi retirado beta 6. Ash colocá-lo novamente embora ... github.com/AshFurrow/hasValueOpcionais implicitamente desembrulhados são úteis para apresentar uma propriedade como não opcional quando realmente precisa ser opcional sob as cobertas. Isso geralmente é necessário para "dar um nó" entre dois objetos relacionados, que precisam de uma referência para o outro. Faz sentido quando nenhuma referência é realmente opcional, mas uma delas precisa ser nula enquanto o par está sendo inicializado.
Por exemplo:
Qualquer
B
instância deve sempre ter umamyBuddyA
referência válida ; portanto, não queremos que o usuário a trate como opcional, mas precisamos que ela seja opcional para que possamos construir aB
antes de termos umaA
referência.CONTUDO! Esse tipo de requisito de referência mútua geralmente é uma indicação de acoplamento rígido e design deficiente. Se você se deparar com opcionais implicitamente desembrulhados, provavelmente deverá considerar a refatoração para eliminar as dependências cruzadas.
fonte
@IBOutlet
weak
declaração e remover "sem criar uma forte reter ciclo"Os opcionais implícitamente desembrulhados são um compromisso pragmático para tornar o trabalho em ambiente híbrido que deve interoperar com as estruturas existentes do Cocoa e suas convenções mais agradáveis, além de permitir a migração gradual para um paradigma de programação mais seguro - sem ponteiros nulos - imposto pelo compilador Swift.
O livro Swift, no capítulo Básico , na seção Implicitamente Desembrulhado Opcional, diz:
Isso se aplica aos casos de uso em que a não-
nil
integridade das propriedades é estabelecida por meio da convenção de uso e não pode ser imposta pelo compilador durante a inicialização da classe. Por exemplo, asUIViewController
propriedades que são inicializadas a partir de NIBs ou Storyboards, onde a inicialização é dividida em fases separadas, mas apósviewDidLoad()
você pode assumir que as propriedades geralmente existem. Caso contrário, para satisfazer o compilador, era necessário usar o desempacotamento forçado , a ligação opcional ou o encadeamento opcional apenas para ocultar o objetivo principal do código.A parte acima do livro Swift se refere também ao capítulo Contagem automática de referência :
Isso se resume às peculiaridades de não ser uma linguagem de coleta de lixo; portanto, a interrupção dos ciclos de retenção é uma tarefa do programador e os opcionais implicitamente desembrulhados são uma ferramenta para ocultar essa peculiaridade.
Isso abrange os itens "Quando usar opcionais implicitamente desembrulhados no seu código?" questão. Como desenvolvedor de aplicativos, você os encontrará principalmente em assinaturas de método de bibliotecas escritas em Objective-C, que não têm a capacidade de expressar tipos opcionais.
Em Usando Swift com cacau e Objective-C, seção Trabalhando com zero :
... e além daqui jazia
fonte
Exemplos simples de uma linha (ou várias linhas) não cobrem muito bem o comportamento dos opcionais - sim, se você declarar uma variável e fornecer um valor imediatamente, não há sentido em um opcional.
O melhor caso que eu vi até agora é a configuração que ocorre após a inicialização do objeto, seguida pelo uso "garantido" para seguir essa configuração, por exemplo, em um controlador de exibição:
Sabemos
printSize
que será chamado depois que a exibição for carregada - é um método de ação conectado a um controle dentro dessa exibição e tomamos o cuidado de não chamá-lo de outra forma. Para que possamos economizar algumas verificações / ligações opcionais com o!
. Swift não pode reconhecer essa garantia (pelo menos até que a Apple resolva o problema de parada), então você diz ao compilador que ela existe.Isso interrompe a segurança do tipo em algum grau, no entanto. Qualquer lugar em que você tenha um opcional implicitamente desembrulhado é um local em que seu aplicativo pode travar se a sua "garantia" nem sempre for válida, portanto, é um recurso a ser usado com moderação. Além disso, usar
!
o tempo todo faz parecer que você está gritando, e ninguém gosta disso.fonte
CGSizeZero
pode ser um bom valor de sentinela no uso no mundo real. Mas e se você tiver um tamanho carregado da ponta que possa realmente ser zero? Então, usarCGSizeZero
como sentinela não ajuda a distinguir entre um valor não definido e definido como zero. Além disso, esta aplica-se igualmente bem a outros tipos carregados de bico (ou em qualquer outro lugar depoisinit
): cordas, as referências a subvisualizações, etc.let foo? = 42
, nãolet foo! = 42
). Isso não resolve isso. (Isto pode ser uma resposta relevante sobre opcionais, você mente, mas não opcionais sobre implicitamente desembrulhados que são um / animal relacionado diferente.)A Apple fornece um excelente exemplo em The Swift Programming Language -> Contagem automática de referências -> Resolvendo ciclos de referência fortes entre instâncias de classe -> Referências não proprietárias e propriedades opcionais implicitamente desembrulhadas
fonte
A lógica dos opcionais implícitos é mais fácil de explicar, olhando primeiro a lógica do desembrulhamento forçado.
Desembrulhamento forçado de um opcional (implícito ou não), usando o! operador, significa que você tem certeza de que seu código não possui bugs e o opcional já possui um valor em que está sendo desembrulhado. Sem o! operador, você provavelmente apenas afirmaria com uma ligação opcional:
o que não é tão bom quanto
Agora, os opcionais implícitos permitem expressar um opcional que você sempre espera ter um valor quando desembrulhado, em todos os fluxos possíveis. Por isso, apenas vai um passo além em ajudá-lo - relaxando a exigência de escrever o! para desembrulhar a cada vez e garantir que o tempo de execução ainda erro, caso suas suposições sobre o fluxo estejam incorretas.
fonte
init
(as quais você talvez nem implemente), mas elas ' é garantido que será definido apósawakeFromNib
/viewDidLoad
.Se você tiver certeza, um valor retornado por um opcional, em vez de
nil
, Opcionais Implicitamente Desembrulhados usa para capturar diretamente esses valores de opcionais e não opcionais.Portanto, esta é a diferença entre o uso de
let someString : String!
elet someString : String
fonte
Eu acho que
Optional
é um nome ruim para essa construção que confunde muitos iniciantes.Outros idiomas (Kotlin e C # por exemplo) usam o termo
Nullable
e facilita muito a compreensão disso.Nullable
significa que você pode atribuir um valor nulo a uma variável desse tipo. Portanto, se forNullable<SomeClassType>
, você pode atribuir nulos a ele, se for apenasSomeClassType
, você não pode. É assim que o Swift funciona.Por que usá-los? Bem, às vezes você precisa ter nulos, é por isso. Por exemplo, quando você sabe que deseja ter um campo em uma classe, mas não pode atribuí-lo a nada quando estiver criando uma instância dessa classe, mas o fará posteriormente. Não vou dar exemplos, porque as pessoas já os forneceram aqui. Só estou escrevendo isso para dar meus 2 centavos.
A propósito, sugiro que você veja como isso funciona em outros idiomas, como Kotlin e C #.
Aqui está um link que explica esse recurso no Kotlin: https://kotlinlang.org/docs/reference/null-safety.html
Outras linguagens, como Java e Scala, têm
Optional
s, mas funcionam de forma diferente deOptional
s no Swift, porque os tipos de Java e Scala são todos anuláveis por padrão.Em suma, acho que esse recurso deveria ter sido nomeado
Nullable
em Swift, e nãoOptional
...fonte
Implicitly Unwrapped Optional
é um açúcar sintático, poisOptional
não força um programador a desembrulhar uma variável. Pode ser usado para uma variável que não pode ser inicializada durantetwo-phase initialization process
e implica um valor nulo. Essa variável se comporta como não nula, mas na verdade é uma variável opcional . Um bom exemplo é - as tomadas do Interface BuilderOptional
geralmente são preferíveisfonte