Mar*_*rte 129 ios associated-object swift
我正在将一个应用程序从Objective-C切换到Swift,我有几个带有存储属性的类别,例如:
@interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
@property (strong) PFObject *xo;
@property (nonatomic) BOOL isAnimating;
@end
Run Code Online (Sandbox Code Playgroud)
由于Swift扩展不接受这些存储的属性,我不知道如何维护与Objc代码相同的结构.存储的属性对我的应用程序非常重要,我相信Apple必须在Swift中创建一些解决方案.
正如jou所说,我所寻找的实际上是使用关联对象,所以我做了(在另一个上下文中):
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))
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是在执行以下操作时我得到了EXC_BAD_ACCESS:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
jou*_*jou 186
与Objective-C一样,您无法将存储的属性添加到现有类中.如果您正在扩展Objective-C类(UIView
肯定是一个),您仍然可以使用关联对象来模拟存储的属性:
对于Swift 1
import ObjectiveC
private var xoAssociationKey: UInt8 = 0
extension UIView {
var xo: PFObject! {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
}
set(newValue) {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
}
}
}
Run Code Online (Sandbox Code Playgroud)
关联键是一个指针,应该是每个关联的唯一指针.为此,我们创建一个私有全局变量,并使用它的内存地址作为&
运算符的键.有关如何在Swift中处理指针的详细信息,请参阅使用Swift with Cocoa和Objective-C
.
更新为Swift 2和3
import ObjectiveC
private var xoAssociationKey: UInt8 = 0
extension UIView {
var xo: PFObject! {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
}
set(newValue) {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新为Swift 4
在Swift 4中,它更加简单.Holder结构将包含我们的计算属性将向世界公开的私有值,从而产生存储属性行为的错觉.
extension UIViewController {
struct Holder {
static var _myComputedProperty:Bool = false
}
var myComputedProperty:Bool {
get {
return Holder._myComputedProperty
}
set(newValue) {
Holder._myComputedProperty = newValue
}
}
}
Run Code Online (Sandbox Code Playgroud)
Woj*_*zki 47
关联对象API使用起来有点麻烦.您可以使用帮助程序类删除大部分样板文件.
public final class ObjectAssociation<T: AnyObject> {
private let policy: objc_AssociationPolicy
/// - Parameter policy: An association policy that will be used when linking objects.
public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
self.policy = policy
}
/// Accesses associated object.
/// - Parameter index: An object whose associated object is to be accessed.
public subscript(index: AnyObject) -> T? {
get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
}
}
Run Code Online (Sandbox Code Playgroud)
前提是您可以以更易读的方式将属性"添加"到objective-c类:
extension SomeType {
private static let association = ObjectAssociation<NSObject>()
var simulatedProperty: NSObject? {
get { return SomeType.association[self] }
set { SomeType.association[self] = newValue }
}
}
Run Code Online (Sandbox Code Playgroud)
至于解决方案:
extension CALayer {
private static let initialPathAssociation = ObjectAssociation<CGPath>()
private static let shapeLayerAssociation = ObjectAssociation<CAShapeLayer>()
var initialPath: CGPath! {
get { return CALayer.initialPathAssociation[self] }
set { CALayer.initialPathAssociation[self] = newValue }
}
var shapeLayer: CAShapeLayer? {
get { return CALayer.shapeLayerAssociation[self] }
set { CALayer.shapeLayerAssociation[self] = newValue }
}
}
Run Code Online (Sandbox Code Playgroud)
Ale*_*exK 35
所以我认为我找到了一种比上面的方法更干净的方法,因为它不需要任何全局变量.我从这里得到它:http: //nshipster.com/swift-objc-runtime/
要点是你使用这样的结构:
extension UIViewController {
private struct AssociatedKeys {
static var DescriptiveName = "nsh_DescriptiveName"
}
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Swift 2的更新
private struct AssociatedKeys {
static var displayed = "displayed"
}
//this lets us check to see if the item is supposed to be displayed or not
var displayed : Bool {
get {
guard let number = objc_getAssociatedObject(self, &AssociatedKeys.displayed) as? NSNumber else {
return true
}
return number.boolValue
}
set(value) {
objc_setAssociatedObject(self,&AssociatedKeys.displayed,NSNumber(bool: value),objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
Run Code Online (Sandbox Code Playgroud)
Hep*_*Kes 14
jou指出的解决方案不支持值类型,这也适用于它们
包装
import ObjectiveC
final class Lifted<T> {
let value: T
init(_ x: T) {
value = x
}
}
private func lift<T>(x: T) -> Lifted<T> {
return Lifted(x)
}
func setAssociatedObject<T>(object: AnyObject, value: T, associativeKey: UnsafePointer<Void>, policy: objc_AssociationPolicy) {
if let v: AnyObject = value as? AnyObject {
objc_setAssociatedObject(object, associativeKey, v, policy)
}
else {
objc_setAssociatedObject(object, associativeKey, lift(value), policy)
}
}
func getAssociatedObject<T>(object: AnyObject, associativeKey: UnsafePointer<Void>) -> T? {
if let v = objc_getAssociatedObject(object, associativeKey) as? T {
return v
}
else if let v = objc_getAssociatedObject(object, associativeKey) as? Lifted<T> {
return v.value
}
else {
return nil
}
}
Run Code Online (Sandbox Code Playgroud)
可能的 类扩展(使用示例):
extension UIView {
private struct AssociatedKey {
static var viewExtension = "viewExtension"
}
var referenceTransform: CGAffineTransform? {
get {
return getAssociatedObject(self, associativeKey: &AssociatedKey.viewExtension)
}
set {
if let value = newValue {
setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.viewExtension, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这真是一个非常好的解决方案,我想添加另一个使用示例,其中包含非选项的结构和值.此外,可以简化AssociatedKey值.
struct Crate {
var name: String
}
class Box {
var name: String
init(name: String) {
self.name = name
}
}
extension UIViewController {
private struct AssociatedKey {
static var displayed: UInt8 = 0
static var box: UInt8 = 0
static var crate: UInt8 = 0
}
var displayed: Bool? {
get {
return getAssociatedObject(self, associativeKey: &AssociatedKey.displayed)
}
set {
if let value = newValue {
setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.displayed, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
var box: Box {
get {
if let result:Box = getAssociatedObject(self, associativeKey: &AssociatedKey.box) {
return result
} else {
let result = Box(name: "")
self.box = result
return result
}
}
set {
setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.box, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var crate: Crate {
get {
if let result:Crate = getAssociatedObject(self, associativeKey: &AssociatedKey.crate) {
return result
} else {
let result = Crate(name: "")
self.crate = result
return result
}
}
set {
setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.crate, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ada*_*ght 13
您无法使用新存储定义类别(Swift扩展); 必须计算任何其他属性而不是存储.该语法适用于Objective C,因为@property
在一个类别中,本质上意味着"我将提供getter和setter".在Swift中,您需要自己定义这些以获取计算属性; 就像是:
extension String {
public var Foo : String {
get
{
return "Foo"
}
set
{
// What do you want to do here?
}
}
}
Run Code Online (Sandbox Code Playgroud)
应该工作正常.请记住,您无法在setter中存储新值,只能使用现有的可用类状态.
我的0.02美元.此代码是用Swift 2.0编写的
extension CALayer {
private struct AssociatedKeys {
static var shapeLayer:CAShapeLayer?
}
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.shapeLayer) as? CAShapeLayer
}
set {
if let newValue = newValue {
objc_setAssociatedObject(self, &AssociatedKeys.shapeLayer, newValue as CAShapeLayer?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试了很多解决方案,发现这是实际扩展具有额外变量参数的类的唯一方法.
我更喜欢在纯Swift中编写代码而不依赖于Objective-C遗产.因此,我编写了纯Swift解决方案,它有两个优点和两个缺点.
好处:
纯Swift代码
适用于类和完成,或更具体地说,Any
对象
缺点:
代码应该调用方法willDeinit()
来释放链接到特定类实例的对象,以避免内存泄漏
你不能直接向UIView扩展这个确切的例子,因为它var frame
是UIView的扩展,而不是类的一部分.
import UIKit
var extensionPropertyStorage: [NSObject: [String: Any]] = [:]
var didSetFrame_ = "didSetFrame"
extension UILabel {
override public var frame: CGRect {
get {
return didSetFrame ?? CGRectNull
}
set {
didSetFrame = newValue
}
}
var didSetFrame: CGRect? {
get {
return extensionPropertyStorage[self]?[didSetFrame_] as? CGRect
}
set {
var selfDictionary = extensionPropertyStorage[self] ?? [String: Any]()
selfDictionary[didSetFrame_] = newValue
extensionPropertyStorage[self] = selfDictionary
}
}
func willDeinit() {
extensionPropertyStorage[self] = nil
}
}
Run Code Online (Sandbox Code Playgroud)
为什么要依赖objc运行时?我不明白这一点.通过使用类似下面的内容,您将通过仅使用纯Swift方法实现几乎相同的存储属性行为:
extension UIViewController {
private static var _myComputedProperty = [String:Bool]()
var myComputedProperty:Bool {
get {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
return UIViewController._myComputedProperty[tmpAddress] ?? false
}
set(newValue) {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
UIViewController._myComputedProperty[tmpAddress] = newValue
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
101865 次 |
最近记录: |