Qual é a diferença entre uma referência fraca e uma referência não proprietária?

240

Swift tem:

  • Referências Fortes
  • Referências fracas
  • Referências não proprietárias

Qual é a diferença entre uma referência não proprietária e uma referência fraca?

Quando é seguro usar uma referência não proprietária?

As referências não proprietárias são um risco de segurança, como ponteiros pendentes em C / C ++?

Ian Ringrose
fonte
3
Artigo muito bom sobre andrewcbancroft.com/2015/05/08/…
Zeeshan
A minha experiência é usar unownedpara as classes que controlam, para as classes da Apple, o uso weak, porque não podemos garantir ao certo o que ele faz
onmyway133
@NoorAli, ou "ownedBy" como a referência "sem dono" geralmente aponta para o proprietário.
Ian Ringrose

Respostas:

361

As referências weake unownednão criam uma strongretenção no objeto referido (também conhecido como não aumentam a contagem de retenção para impedir que o ARC desaloque o objeto referido).

Mas por que duas palavras-chave? Essa distinção tem a ver com o fato de os Optionaltipos serem integrados na linguagem Swift. Para encurtar a história: tipos opcionais oferecem segurança à memória (isso funciona muito bem com as regras de construtor de Swift - que são rígidas para fornecer esse benefício).

Uma weakreferência permite a possibilidade de se tornar nil(isso acontece automaticamente quando o objeto referenciado é desalocado); portanto, o tipo de sua propriedade deve ser opcional - para que você, como programador, seja obrigado a verificá-la antes de usá-lo (basicamente o O compilador obriga você, na medida do possível, a escrever um código seguro).

Uma unownedreferência presume que nunca se tornará nildurante sua vida útil. Uma referência não proprietária deve ser definida durante a inicialização - isso significa que a referência será definida como um tipo não opcional que pode ser usado com segurança sem verificações. Se, de alguma forma, o objeto que está sendo referido for desalocado, o aplicativo falhará quando a referência não proprietária for usada.

Dos documentos da Apple :

Use uma referência fraca sempre que for válido que essa referência se torne nula em algum momento durante sua vida útil. Por outro lado, use uma referência não proprietária quando souber que a referência nunca será nula depois de definida durante a inicialização.

Nos documentos, existem alguns exemplos que discutem ciclos de retenção e como quebrá-los. Todos esses exemplos são extraídos dos documentos .

Exemplo da weakpalavra-chave:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var tenant: Person?
}

E agora, para algumas obras de arte ASCII (você deve consultar os documentos - eles têm belos diagramas):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

O exemplo Persone Apartmentmostra uma situação em que duas propriedades, que são permitidas nulas, têm o potencial de causar um forte ciclo de referência. Este cenário é melhor resolvido com uma referência fraca. Ambas as entidades podem existir sem ter uma dependência estrita da outra.

Exemplo da unownedpalavra-chave:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}

Neste exemplo, a Customerpode ou não ter a CreditCard, mas a CreditCard sempre será associado a a Customer. Para representar isso, a Customerclasse possui uma cardpropriedade opcional , mas a CreditCardclasse possui uma propriedade não opcional (e não proprietária) customer.

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

O exemplo Customere CreditCardmostra uma situação em que uma propriedade que pode ser nula e outra que não pode ser nula têm o potencial de causar um forte ciclo de referência. Este cenário é melhor resolvido com uma referência não proprietária.

Nota da Apple:

Referências fracas devem ser declaradas como variáveis, para indicar que seu valor pode mudar em tempo de execução. Uma referência fraca não pode ser declarada como constante.

Há também um terceiro cenário em que ambas as propriedades devem sempre ter um valor e nenhuma deve ser nula depois que a inicialização estiver concluída.

E também há os cenários clássicos de ciclo de retenção a serem evitados ao trabalhar com fechamentos.

Para isso, recomendo que você visite os documentos da Apple ou leia o livro .

Ilea Cristian
fonte
3
Isso é um tanto trivial, mas acho o exemplo de Apartamento e Pessoa um tanto confuso, que também apresenta uma solução adicional para interromper o forte ciclo de referência. O apartamento de uma pessoa é opcional e, portanto, pode ser nulo, assim como o inquilino de um apartamento é opcional e, portanto, pode ser nulo, portanto, ambas as propriedades podem ser definidas como fracas. ``
Justin Levi Winter
classe Pessoa {let name: String init (nome: String) {self.name = name} fraca var apartment: Apartment? } classe Apartamento {let number: Int init (número: Int) {self.number = number} fraco var tenant: Person? }
Justin Levi Winter
3
Qual é a diferença entre weak var Person?vs.var Person? ?
317 Dean
4
@ JustinLevi, se você declarar ambas as propriedades como fracas, existe a possibilidade de serem desalocadas. A Pessoa mantém uma forte referência ao Apartamento, para que o Apartamento não seja desalocado. Se o apartamento tivesse a mesma referência forte em relação à Pessoa, eles criariam um ciclo de retenção - que pode ser interrompido pelo programador em tempo de execução se ele souber, mas, caso contrário, é apenas um vazamento de memória. Esse é todo o barulho sobre forte, fraco e sem dono: gerenciamento de memória em um nível superior, porque o ARC faz todo o material sujo para nós. Evitar ciclos de retenção é o nosso trabalho.
Ilea Cristian
1
O único benefício dos não proprietários sobre os fracos é que você não precisa desembrulhar e pode usar uma constante? Existe algum exemplo em que você não pode usar fraco e só pode usar sem dono?
27718 Alan Alan
29

Q1 Qual é a diferença entre uma “referência não proprietária” e uma “referência fraca”?

Referência fraca:

Uma referência fraca é uma referência que não mantém uma forte presença na instância a que se refere e, portanto, não impede o ARC de descartar a instância referenciada. Como as referências fracas podem ter "nenhum valor", você deve declarar todas as referências fracas como tendo um tipo opcional. (Documentos da Apple)

Referência não proprietária:

Como referências fracas, uma referência não proprietária não mantém uma forte presença na instância a que se refere. Ao contrário de uma referência fraca, no entanto, supõe-se que uma referência sem dono sempre tenha um valor. Por esse motivo, uma referência não proprietária é sempre definida como um tipo não opcional. (Documentos da Apple)

Quando usar cada um:

Use uma referência fraca sempre que for válido que essa referência se torne nula em algum momento durante sua vida útil. Por outro lado, use uma referência não proprietária quando souber que a referência nunca será nula depois de definida durante a inicialização.(Documentos da Apple)


Q2 Quando é seguro usar uma "referência não proprietária"?

Como citado acima, presume-se que uma referência não proprietária sempre tenha um valor. Portanto, você deve usá-lo apenas quando tiver certeza de que a referência nunca será nula. O Apple Docs ilustra um caso de uso para referências não proprietárias através do exemplo a seguir.

Suponha que temos duas classes Customere CreditCard. Um cliente pode existir sem cartão de crédito, mas um cartão de crédito não existirá sem um cliente, ou seja, pode-se supor que um cartão de crédito sempre terá um cliente. Portanto, eles devem ter o seguinte relacionamento:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3 A referência não proprietária faz referência a um risco de segurança como "ponteiros pendentes" em C / C ++

Acho que não.

Como as referências não proprietárias são apenas referências fracas que garantem um valor, não deve ser um risco à segurança. No entanto, se você tentar acessar uma referência não proprietária após a desalocação da instância que ela referencia, você acionará um erro de tempo de execução e o aplicativo falhará.

Esse é o único risco que vejo com isso.

Link para o Apple Docs

Myxtic
fonte
seu Q2 exemplo de programa simples de entender sobre unowned..thanks .. Você pode adicionar mesmo tipo de exemplo para fraca e forte ..
Ranjith Kumar
Excelente. Obrigado.
Swifty McSwifterton
Você pode incluir um exemplo comum para não dono ou fraco?
Mel
Considere os objetos pai e filho, se o filho não puder existir sem um pai, use-o unownedpara a propriedade de pai na classe filho. fraco é vice-versa. Boa explicação @myxtic! unownedreferências são apenas weakreferências que garantem um valor!
Saif
26

Se o eu puder ser nulo no fechamento, use [eu fraco] .

Se o eu nunca for nulo no fechamento, use [eu sem dono] .

Se está travando quando você usa [self sem dono] , o self provavelmente é nulo em algum momento desse fechamento e você provavelmente precisa usar [self fraco] .

Confira os exemplos de uso de fortes , fracos e sem dono em fechamentos:

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html

TenaciousJay
fonte
7
Por que não usar apenas fraco, mesmo que o eu nunca possa ser nulo, nenhum dano é certo?
Boon
4
oi @Boon - essa é realmente a questão crítica.
Fattie
[self fraco] => Se eu usar o fechamento dentro de viewDidLoad (), como pode selfser nulo?
Hassan Tareq
@HassanTareq, acho que alguns bons exemplos são mencionados no artigo mencionado acima. Consulte a seção "Resolvendo ciclos de referência fortes para fechamentos", esp. Citação: "O Swift exige que você escreva self.someProperty ou self.someMethod () (em vez de apenas someProperty ou someMethod ()) sempre que você se referir a um membro de si mesmo dentro de um fechamento. Isso ajuda a lembrar que é possível se capturar por acidente." Trecho de: Apple Inc. “A linguagem de programação Swift (Swift 4).” iBooks. itunes.apple.com/de/book/the-swift-programming-language-swift-4/… "
Nick Entin
1
@Boon Se você sempre usa fraco, o compilador forçará a verificar se há opcional antes de usar. Caso você não tenha feito essa verificação, ocorrerá um erro no tempo de compilação. Não há outro dano.
Vikas Mishra
5

Extrações do link

Poucos pontos finais

  • Para determinar se você precisa se preocupar com fortes, fracos ou sem dono, pergunte: "Estou lidando com tipos de referência". Se você estiver trabalhando com Structs ou Enums, o ARC não está gerenciando a memória para esses tipos e nem precisa se preocupar em especificar fraco ou não proprietário para essas constantes ou variáveis.
  • Referências fortes são boas em relacionamentos hierárquicos em que o pai faz referência ao filho, mas não vice-versa. De fato, referências fortes são o tipo de referência mais apropriado na maioria das vezes.
  • Quando duas instâncias estão opcionalmente relacionadas uma à outra, verifique se uma dessas instâncias mantém uma referência fraca à outra.
  • Quando duas instâncias são relacionadas de forma que uma delas não possa existir sem a outra, a instância com dependência obrigatória precisa manter uma referência não proprietária à outra instância.
Abhinav Singh
fonte
1

Ambos weake unownedreferências não terá impacto sobre a contagem de referência do objeto. Porém, referências fracas sempre serão opcionais, ou seja, podem ser nulas, enquanto unownedreferências nunca podem ser nulas, portanto nunca serão opcionais. Ao usar uma referência opcional, você sempre terá que lidar com a possibilidade de o objeto ser nulo. No caso de uma referência não proprietária, você deverá garantir que o objeto nunca seja nulo. Usar uma referência não proprietária para um objeto nulo será semelhante a desembrulhar com força um opcional que seja nulo.

Dito isto, é seguro usar uma referência não proprietária, onde você tem certeza de que o tempo de vida do objeto é maior que o da referência. Se não for esse o caso, é melhor usar uma referência fraca.

Quanto à terceira parte da pergunta, não acho que referências sem dono sejam semelhantes a um ponteiro pendente. Quando falamos sobre contagem de referência, geralmente nos referimos à contagem de referência forte do objeto. Da mesma forma, o swift mantém a contagem de referência não proprietária e a contagem de referência fraca para o objeto (pontos de referência fracos para algo chamado "tabela lateral" em vez do próprio objeto). Quando a contagem de referência forte chega a zero, o objeto é desinicializado, mas não pode ser desalocado se a contagem de referência não proprietária for maior que zero.

Agora, um ponteiro pendente é algo que aponta para um local de memória que já foi desalocado. Mas rapidamente, uma vez que a memória só pode ser desalocada enquanto houver uma referência não proprietária ao objeto, ela não pode causar um ponteiro pendente.

Existem muitos artigos que discutem o gerenciamento rápido de memória em mais detalhes. Aqui está um.

Deeksha Kaul
fonte
0

Referências não proprietárias são um tipo de referência fraca usada no caso de um relacionamento Same-Lifetime entre dois objetos, quando um objeto só deve pertencer a outro objeto. É uma maneira de criar uma ligação imutável entre um objeto e uma de suas propriedades.

No exemplo dado no vídeo WWDC rápido intermediário, uma pessoa possui um cartão de crédito, e um cartão de crédito pode ter apenas um titular. No cartão de crédito, a pessoa não deve ser uma propriedade opcional, porque você não deseja ter um cartão de crédito flutuando com apenas um proprietário. Você pode interromper esse ciclo tornando a propriedade do titular no crédito uma referência fraca, mas isso também exige que você a torne opcional e variável (e não constante). A referência não proprietária nesse caso significa que, embora o CreditCard não possua uma participação acionária em uma Pessoa, sua vida depende disso.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}
JuJoDi
fonte
link para o vídeo ou título wwdc?
Osa
-2

Use unownedquando tiver certeza de selfque nunca poderá estar nilno ponto em que acessa selfnesse momento.

Exemplo (é claro que você pode adicionar o destino diretamente de MyViewController, mas, novamente, é um exemplo simples):

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myButton = MyButton { [unowned self] in
            print("At this point, self can NEVER be nil. You are safe to use unowned.")
            print("This is because myButton can not be referenced without/outside this instance (myViewController)")
        }
    }
}

class MyButton: UIButton {
    var clicked: (() -> ())

    init(clicked: (() -> ())) {
        self.clicked = clicked

        // We use constraints to layout the view. We don't explicitly set the frame.
        super.init(frame: .zero)

        addTarget(self, action: #selector(clicked), for: .touchUpInside)
    }

    @objc private func sendClosure() {
        clicked()
    }
}

Use weakquando houver a possibilidade de selfestar nilno ponto que você está acessando self.

Exemplo:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
            print("Can you guarentee that self is always available when the network manager received data?")
            print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
            print("You should use weak self here, since you are not sure if this instance is still alive for every")
            print("future callback of network manager")
        }
    }
}

class NetworkManager {

    static let sharedInstance = NetworkManager()

    var receivedData: ((Data) -> ())?

    private func process(_ data: Data) {
        // process the data...

        // ... eventually notify a possible listener.
        receivedData?(data)
    }
}

Contras de unowned:

  • Mais eficiente que fraco
  • Você pode (bem, você é forçado) a marcar a instância como imutável (não mais desde o Swift 5.0).
  • Indica ao leitor do seu código: Esta instância tem um relacionamento com X e não pode viver sem ele, mas se X desapareceu, eu também desapareci.

Contras de weak:

  • Mais seguro do que sem dono (já que não pode falhar).
  • Pode criar um relacionamento com o X de ambos os modos, mas ambos podem viver um sem o outro.

Se você não tiver certeza, use weak. Espere , quero dizer, pergunte aqui no StackOverflow o que você deve fazer no seu caso! Usar fraco o tempo todo quando você não deveria é apenas confuso para você e para o leitor do seu código.

J. Doe
fonte