Estou mudando um aplicativo do Objective-C para o Swift, que tenho algumas categorias com propriedades armazenadas, por exemplo:
@interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
@property (strong) PFObject *xo;
@property (nonatomic) BOOL isAnimating;
@end
Como as extensões Swift não aceitam propriedades armazenadas como essas, não sei como manter a mesma estrutura que o código Objc. As propriedades armazenadas são realmente importantes para o meu aplicativo e acredito que a Apple deve ter criado alguma solução para fazê-lo no Swift.
Como dito por jou, o que eu estava procurando era realmente usar objetos associados, então usei (em outro contexto):
import Foundation
import QuartzCore
import ObjectiveC
extension CALayer {
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, "shapeLayer") as? CAShapeLayer
}
set(newValue) {
objc_setAssociatedObject(self, "shapeLayer", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
var initialPath: CGPathRef! {
get {
return objc_getAssociatedObject(self, "initialPath") as CGPathRef
}
set {
objc_setAssociatedObject(self, "initialPath", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
}
Mas eu recebo um EXC_BAD_ACCESS ao fazer:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
Alguma ideia?
ios
swift
associated-object
Marcos Duarte
fonte
fonte
Respostas:
A API de objetos associados é um pouco complicada de usar. Você pode remover a maior parte do clichê com uma classe auxiliar.
Desde que você possa "adicionar" uma propriedade à classe objetivo-c de maneira mais legível:
Quanto à solução:
fonte
NSNumber
ouNSValue
e escrever pares adicionais de acessadores que seriam do tipo desejado (Int, Bool, etc).NSObjectProtocol
[String]?
é uma estrutura, é o mesmo caso do Int, Bool. Você pode usarNSArray
para manter uma coleção deNSString
instâncias.Como no Objective-C, você não pode adicionar propriedades armazenadas a classes existentes. Se você estiver estendendo uma classe Objective-C (
UIView
é definitivamente uma), ainda poderá usar os Objetos Associados para emular as propriedades armazenadas:para Swift 1
A chave da associação é um ponteiro que deve ser exclusivo para cada associação. Para isso, criamos uma variável global privada e usamos seu endereço de memória como a chave do
&
operador. Consulte Usando o Swift com cacau e Objective-C para obter mais detalhes sobre como os ponteiros são manipulados no Swift.ATUALIZADO para Swift 2 e 3
ATUALIZADO para Swift 4
No Swift 4, é muito mais simples. A estrutura Holder conterá o valor privado que nossa propriedade computada expõe ao mundo, dando a ilusão de um comportamento de propriedade armazenada.
Fonte
fonte
objc_AssociationPolicy
, obrigado! Atualizei a respostaEntão, acho que encontrei um método que funciona mais limpo que os acima, porque não requer nenhuma variável global. Eu peguei aqui: http://nshipster.com/swift-objc-runtime/
A essência é que você usa uma estrutura assim:
ATUALIZAÇÃO para Swift 2
fonte
A solução apontada por jou não suporta tipos de valor , isso também funciona bem com eles
Wrappers
Uma possível extensão de classe (exemplo de uso):
Esta é realmente uma ótima solução, eu queria adicionar outro exemplo de uso que incluísse estruturas e valores que não são opcionais. Além disso, os valores AssociatedKey podem ser simplificados.
fonte
lift
método e aLifted
classe, mas devem usar as funçõesgetAssociatedObject
&setAssociatedObject
. Vou adicionar "Exemplo de uso" entre parênteses ao lado do cabeçalho por uma questão de clareza.Você não pode definir categorias (extensões Swift) com novo armazenamento; quaisquer propriedades adicionais devem ser calculadas em vez de armazenadas. A sintaxe funciona para o objetivo C, porque
@property
em uma categoria significa essencialmente "fornecerei o getter e o setter". No Swift, você precisará defini-las para obter uma propriedade computada; algo como:Deve funcionar bem. Lembre-se, você não pode armazenar novos valores no setter, apenas trabalhar com o estado de classe disponível existente.
fonte
Meus US $ 0,02. Este código está escrito no Swift 2.0
Eu tentei muitas soluções e descobri que essa é a única maneira de realmente estender uma classe com parâmetros variáveis adicionais.
fonte
type 'AssociatedKeys' cannot be defined within a protocol extension
...Por que confiar no objc runtime? Eu não entendi o ponto. Usando algo como o seguinte, você obterá quase o comportamento idêntico de uma propriedade armazenada, usando apenas uma abordagem Swift pura :
fonte
Prefiro fazer código no Swift puro e não confiar na herança do Objective-C. Por isso, escrevi a solução Swift pura com duas vantagens e duas desvantagens.
Vantagens:
Código Swift puro
Funciona em classes e conclusões ou mais especificamente em
Any
objetosDesvantagens:
O código deve chamar o método
willDeinit()
para liberar objetos vinculados a instância de classe específica para evitar vazamentos de memóriaVocê não pode fazer a extensão diretamente no UIView para este exemplo exato, porque
var frame
é uma extensão do UIView, não faz parte da classe.EDITAR:
fonte
extensionPropertyStorage
é compartilhado com todas as instâncias por design. É a variável global (Dictionary) que primeiro desassocia a instância UILabel (NSObject
) e depois sua propriedade ([String: Any]
).class
propriedades;)frame
é uma propriedade de instância. De onde vem a propriedade da classe?Com as categorias Obj-c, você pode adicionar apenas métodos, não variáveis de instância.
No exemplo, você usou @property como um atalho para adicionar declarações de método getter e setter. Você ainda precisa implementar esses métodos.
Da mesma forma, no Swift, você pode adicionar extensões de uso para adicionar métodos de instância, propriedades calculadas etc., mas não propriedades armazenadas.
fonte
Também recebo um problema EXC_BAD_ACCESS.O valor em
objc_getAssociatedObject()
eobjc_setAssociatedObject()
deve ser um objeto. E oobjc_AssociationPolicy
deve corresponder ao objeto.fonte
Tentei usar objc_setAssociatedObject conforme mencionado em algumas das respostas aqui, mas depois de falhar algumas vezes, dei um passo atrás e percebi que não havia razão para isso. Tomando emprestado algumas das idéias aqui, criei esse código que simplesmente armazena uma matriz com quaisquer dados extras (MyClass neste exemplo) indexados pelo objeto ao qual quero associá-lo:
fonte
Aqui está a solução simplificada e mais expressiva. Funciona para os tipos de valor e referência. A abordagem do levantamento é retirada da resposta @HepaKKes.
Código da associação:
Exemplo de uso:
1) Crie uma extensão e associe propriedades a ela. Vamos usar as propriedades do tipo valor e referência.
2) Agora você pode usar apenas as propriedades regulares:
Mais detalhes:
fonte
Outro exemplo com o uso de objetos associados ao Objective-C e propriedades calculadas para Swift 3 e Swift 4
fonte
se você deseja definir um atributo de string personalizado para um UIView, foi assim que eu o fiz no Swift 4
Crie uma extensão UIView
Para usar esta extensão
fonte
Que tal armazenar o mapa estático na classe que está se estendendo assim:
fonte
Tentei armazenar propriedades usando objc_getAssociatedObject, objc_setAssociatedObject, sem sorte. Meu objetivo era criar uma extensão para o UITextField, para validar o comprimento dos caracteres de entrada de texto. O código a seguir funciona bem para mim. Espero que isso ajude alguém.
fonte
_min
e_max
é global e será o mesmo em todas as instâncias do UITextField? Mesmo que funcione para você, essa resposta não tem relação porque Marcos pergunta sobre variáveis de instância .Aqui está uma alternativa que também funciona
fonte
Achei esta solução mais prática
ATUALIZADO para Swift 3
... então no seu código
fonte