Я переключаю приложение с Objective-C на Swift, у меня есть несколько категорий с сохраненными свойствами, например:
@interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
@property (strong) PFObject *xo;
@property (nonatomic) BOOL isAnimating;
@end
Поскольку расширения Swift не принимают хранимые свойства, подобные этим, я не знаю, как поддерживать ту же структуру, что и код Objc. Сохраненные свойства действительно важны для моего приложения, и я считаю, что Apple, должно быть, разработала какое-то решение для этого в Swift.
Как сказал Джоу, то, что я искал, на самом деле использовало связанные объекты, поэтому я сделал (в другом контексте):
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))
}
}
}
Но я получаю EXC_BAD_ACCESS при выполнении:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
Любые идеи?
ios
swift
associated-object
Маркос Дуарте
источник
источник
Ответы:
API связанных объектов немного громоздок в использовании. Вы можете удалить большую часть шаблона с помощью вспомогательного класса.
При условии, что вы можете «добавить» свойство в класс target-c более читабельным образом:
Что касается решения:
источник
NSNumber
илиNSValue
и написать дополнительную пару аксессоров того типа, который вам нужен (Int, Bool и т. Д.).NSObjectProtocol
[String]?
- это структура, это тот же случай, что и для Int, Bool. Вы можете использоватьNSArray
для хранения коллекцииNSString
экземпляров.Как и в Objective-C, вы не можете добавить сохраненное свойство к существующим классам. Если вы расширяете класс Objective-C (
UIView
определенно один из них), вы все равно можете использовать связанные объекты для имитации сохраненных свойств:для Swift 1
Ключ ассоциации - это указатель, который должен быть уникальным для каждой ассоциации. Для этого мы создаем частную глобальную переменную и используем ее адрес памяти в качестве ключа с
&
оператором. См. Использование Swift с Какао и Objective-C для более подробной информации о том, как обрабатываются указатели в Swift.ОБНОВЛЕНО для Swift 2 и 3
ОБНОВЛЕНО для Swift 4
В Swift 4 все намного проще. Структура Holder будет содержать частную ценность, которую наше вычисленное свойство представит миру, создавая иллюзию поведения хранимых свойств.
Источник
источник
objc_AssociationPolicy
, спасибо! Я обновил ответДумаю, я нашел метод, который работает чище, чем приведенные выше, поскольку не требует никаких глобальных переменных. Я получил это отсюда: http://nshipster.com/swift-objc-runtime/
Суть в том, что вы используете такую структуру:
ОБНОВЛЕНИЕ для Swift 2
источник
Решение, на которое указывает jou , не поддерживает типы значений , с ними это тоже хорошо работает
Упаковщики
Возможное расширение класса (пример использования):
Это действительно отличное решение, я хотел добавить еще один пример использования, включающий структуры и значения, которые не являются необязательными. Также можно упростить значения AssociatedKey.
источник
lift
метод иLifted
класс, они должны использовать однакоgetAssociatedObject
&setAssociatedObject
функции. Я добавлю «Пример использования» в скобках рядом с заголовком для ясности.Вы не можете определять категории (расширения Swift) с новым хранилищем; любые дополнительные свойства должны быть вычислены, а не сохранены. Синтаксис работает для Objective C, потому что
@property
в категории по существу означает «Я предоставлю средства получения и установки». В Swift вам нужно определить их самостоятельно, чтобы получить вычисляемое свойство; что-то вроде:Должно работать нормально. Помните, что вы не можете хранить новые значения в установщике, работайте только с существующим доступным состоянием класса.
источник
Мои $ 0,02. Этот код написан на Swift 2.0
Я перепробовал много решений и обнаружил, что это единственный способ расширить класс с помощью дополнительных переменных параметров.
источник
type 'AssociatedKeys' cannot be defined within a protocol extension
...Зачем полагаться на среду выполнения objc? Я не понимаю, в чем суть. Используя что-то вроде следующего, вы достигнете почти идентичного поведения хранимого свойства, используя только чистый подход Swift :
источник
Я предпочитаю писать код на чистом Swift и не полагаться на наследие Objective-C. Из-за этого я написал чистое решение Swift с двумя преимуществами и двумя недостатками.
Преимущества:
Чистый код Swift
Работает над классами и доработками или, точнее, над
Any
объектомНедостатки:
Код должен вызывать метод
willDeinit()
для освобождения объектов, связанных с конкретным экземпляром класса, чтобы избежать утечек памяти.Вы не можете сделать расширение непосредственно для UIView для этого точного примера, потому что
var frame
это расширение UIView, а не часть класса.РЕДАКТИРОВАТЬ:
источник
extensionPropertyStorage
предназначается для всех экземпляров. Это глобальная переменная (Dictionary), которая сначала отменяет ссылку на UILabel instance (NSObject
), а затем на его свойство ([String: Any]
).class
свойств;)frame
является свойством экземпляра. Откуда взялась классовая собственность?С помощью категорий Obj-c вы можете добавлять только методы, но не переменные экземпляра.
В вашем примере вы использовали @property как ярлык для добавления объявлений методов получения и установки. Вам все еще нужно реализовать эти методы.
Аналогично, в Swift вы можете добавить использование расширений для добавления методов экземпляров, вычисляемых свойств и т. Д., Но не сохраненных свойств.
источник
У меня также возникает проблема EXC_BAD_ACCESS. Значение в
objc_getAssociatedObject()
иobjc_setAssociatedObject()
должно быть объектом. Иobjc_AssociationPolicy
должен соответствовать объекту.источник
Я попытался использовать objc_setAssociatedObject, как упоминалось в нескольких ответах здесь, но после неудачных попыток несколько раз отступил и понял, что мне это не нужно. Заимствуя несколько идей здесь, я придумал этот код, который просто хранит массив любых моих дополнительных данных (MyClass в этом примере), индексированных объектом, с которым я хочу связать его:
источник
Вот упрощенное и более выразительное решение. Он работает как для значений, так и для ссылочных типов. Подход к подъему взят из ответа @HepaKKes.
Код ассоциации:
Пример использования:
1) Создайте расширение и свяжите с ним свойства. Давайте использовать свойства как значения, так и ссылочного типа.
2) Теперь вы можете использовать как обычные свойства:
Больше деталей:
источник
Другой пример с использованием объектов, связанных с Objective-C, и вычисленных свойств для Swift 3 и Swift 4
источник
если вы хотите установить пользовательский атрибут строки в UIView, это то, как я сделал это на Swift 4
Создайте расширение UIView
Чтобы использовать это расширение
источник
Как насчет сохранения статической карты в класс, который расширяется следующим образом:
источник
Я попытался сохранить свойства с помощью objc_getAssociatedObject, objc_setAssociatedObject, но безуспешно. Моей целью было создать расширение для UITextField, чтобы проверить длину вводимых символов текста. Следующий код отлично подходит для меня. Надеюсь, это кому-нибудь поможет.
источник
_min
и_max
являются глобальными и будут одинаковыми для всех экземпляров UITextField? Даже если это сработает для вас, этот ответ не имеет отношения, потому что Маркос спрашивает о переменных экземпляра .Вот альтернатива, которая тоже работает
источник
Я нашел это решение более практичным
ОБНОВЛЕНО для Swift 3
... тогда в вашем коде
источник