如何在Swift中存储属性,就像在Objective-C中一样?


132

我将应用程序从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中实现。

正如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))
        }
    }
}

但是我这样做时得到一个EXC_BAD_ACCESS:

class UIBubble : UIView {
    required init(coder aDecoder: NSCoder) {
        ...
        self.layer.shapeLayer = CAShapeLayer()
        ...
    }
}

有任何想法吗?


11
Objective-C类类别也不能定义实例变量,那么如何实现这些属性?
Martin R

不知道为什么访问不正确,但是您的代码不起作用。您正在将不同的值传递给setAssociateObject和getAssociatedObject。使用字符串“ shapeLayer”作为键是错误的,关键是指针(实际上是地址),而不是指向的内容。驻留在不同地址的两个相同的字符串是两个不同的键。回顾Jou的答案,并注意他如何将xoAssociationKey定义为全局变量,因此在设置/获取时它是相同的键/指针。
SafeFastExpressive

Answers:


50

关联对象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) }
    }
}

前提是您可以以更易读的方式将属性“添加”到objective-c类:

extension SomeType {

    private static let association = ObjectAssociation<NSObject>()

    var simulatedProperty: NSObject? {

        get { return SomeType.association[self] }
        set { SomeType.association[self] = newValue }
    }
}

至于解决方案:

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 }
    }
}

确定,如果我想存储Int,Bool等怎么办?
Vyachaslav Gerchicov

1
无法通过对象关联直接存储Swift类型。您可以存储例如NSNumberNSValue并编写其他一对访问器,这些访问器将是您想要的类型(Int,Bool等)。
Wojciech Nagrodzki

1
警告这不适用于结构,因为Objective-C运行时库仅支持符合以下要求的类NSObjectProtocol
Charlton Provatas

2
@CharltonProvatas无法与此API一起使用结构,因为它们不符合AnyObject协议。
Wojciech Nagrodzki

@VyachaslavGerchicov [String]?是一个结构,与Int,Bool的情况相同。您可以NSArray用来保留NSString实例的集合。
Wojciech Nagrodzki

184

像在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))
        }
    }
}

关联密钥是每个关联唯一的指针。为此,我们创建一个私有全局变量,并将其内存地址用作&运算符的键。有关如何在Swift中处理指针的更多详细信息,请参见将Swift与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)
        }
    }
}

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
        }
    }
}

2
@Yar不知道objc_AssociationPolicy,谢谢!我已经更新了答案
2015年

2
在Swift2你必须使用objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN
汤姆

1
@SimplGy,请添加您的编辑内容作为其他答案,而不是将代码编辑为其他人的答案。
日航

11
如果您要拥有使用扩展名属性的多个UIViewController实例,则Swift4解决方案将不起作用。请检查源代码中的更新。
iur

9
Swift 4示例不适用于多个实例,因此可能不应该在此处。
Colin Cornaby

36

因此,我认为我找到了一种比上面的方法更清洁的方法,因为它不需要任何全局变量。我从这里得到的: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)
                )
            }
        }
    }
}

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)
    }
}

@AKWeblS我已经实现了像您一样的代码,但是当更新到swift 2.0时,我得到的错误是无法使用类型为'objc_AssociationPolicy)的参数列表调用类型为'UInt'的初始化程序。下
一条

如何更新Swift 2.0?var postDescription:字符串?{得到{返回objc_getAssociatedObject(self,&AssociatedKeys.postDescription)为?字符串}设置{如果让newValue = newValue {objc_setAssociatedObject(self,&AssociatedKeys.postDescription,newValue as NSString ?, UInt(objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC))}}
user2363025,2015年

1
不起作用:static var表示该属性的值在所有实例中都是相同的
Fry

@fry-它继续为我工作。是的,键是静态的,但是其与特定对象相关联,该对象本身不是全局的。
AlexK

15

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
    }
}

可能的 Class扩展(用法示例):

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)
            }
        }
    }
}

这确实是一个很棒的解决方案,我想添加另一个用法示例,其中包括非可选的结构和值。同样,可以简化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)
        }
    }
}

以及如何使用Lifted类?
3lvis 2015年

你为什么需要那个?做什么的?
HepaKKes

我的意思是一般地说,您将如何使用它,谢谢您的帮助:)能否请您添加一个“如何使用”示例?
3lvis

1
“如何使用”示例将从“可能的类扩展”标题下面开始。我认为用户不需要知道如何使用lift方法和Lifted类,而应该使用getAssociatedObjectsetAssociatedObject函数。为了清楚起见,我将在标题旁边的括号之间添加“用法示例”。
HepaKKes

1
这当然是最好的解决方案,可惜的是它并没有很好地解释。它适用于类,结构和其他值类型。这些属性也不必是可选的。辛苦了
picciano '16

13

您无法使用新的存储空间定义类别(快速扩展);任何其他属性都必须计算而不是存储。该语法适用于Objective C,因为@property在类别中本质上是指“我将提供吸气剂和吸气剂”。在Swift中,您需要自己定义这些属性才能获得计算属性;就像是:

extension String {
    public var Foo : String {
        get
        {
            return "Foo"
        }

        set
        {
            // What do you want to do here?
        }
    }
}

应该工作正常。请记住,您不能在设置器中存储新值,而只能使用现有的可用类状态。


7

我的$ 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)
            }
        }
    }
}

我尝试了许多解决方案,发现这是实际使用额外的可变参数扩展类的唯一方法。


看起来很有趣,但是拒绝不适用于协议,type 'AssociatedKeys' cannot be defined within a protocol extension
Ian Bytchek

6

为什么要依赖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
        }
    }
}

1
聪明!这种方法的一个可能缺点是内存管理。释放宿主对象后,它的属性仍将存在于字典中,如果与许多对象一起使用,则可能在内存使用方面造成昂贵的代价。
Charlton Provatas

可能我们可以保存包装的静态列表变量,其中包含对对象(自身)的弱引用。并从_myComputedProperty词典中删除条目,并从Wrapper的didSet方法中删除包装的静态列表变量(当对象被重新分配时,将调用Wrapper类中的didSet,因为我们的弱引用使用nil设置了新值)。
阿朱那

5

我更喜欢在纯Swift中编写代码,而不依赖于Objective-C传统。因此,我编写了具有两个优点和两个缺点的纯Swift解决方案。

优点:

  1. 纯Swift代码

  2. 适用于课程和补全,或更具体地说,适用于Any对象

缺点:

  1. 代码应调用方法willDeinit()以释放链接到特定类实例的对象,以避免内存泄漏

  2. 对于这个确切的示例,您不能直接对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
    }
}

4
由于extensionPropertyStorage在所有实例之间共享,因此无法使用。如果为一个实例设置一个值,那么您将为所有实例设置该值。
picciano '16

战争不好,因为搞砸了功能。现在它更好了,并且可以按预期工作。
vedrano '16

设计将@picciano extensionPropertyStorage 与所有实例共享。全局变量(Dictionary)首先取消引用UILabel实例(NSObject),然后再取消其属性([String: Any])。
vedrano '16

@picciano我猜这对class房地产
有用

frame是实例属性。阶级财产从何而来?
vedrano '16

3

使用Obj-c类别,您只能添加方法,而不能添加实例变量。

在您的示例中,您已经使用@property作为添加getter和setter方法声明的快捷方式。您仍然需要实现这些方法。

同样,在Swift中,您可以添加使用扩展来添加实例方法,计算属性等,但不能添加存储的属性。


2

我也遇到一个EXC_BAD_ACCESS问题,objc_getAssociatedObject()并且中的值objc_setAssociatedObject()应该是一个对象。并且objc_AssociationPolicy应该匹配对象。


3
这并不能真正回答问题。如果您有其他问题,可以点击提问进行提问。一旦您有足够的声誉,您还可以悬赏以吸引更多对此问题的关注。- 评分
AtheistP3ace

@ AtheistP3ace我对此感到非常抱歉。我尝试对此问题添加评论。但是我没有足够的声誉。所以我尝试回答这个问题。
RuiKQ

2

我尝试使用objc_setAssociatedObject(如此处的一些答案中所述),但是几次失败后,我退后一步,意识到没有理由我不需要它。从这里的一些想法中汲取灵感,我想到了这段代码,该代码仅存储我想要与之关联的对象索引的所有额外数据的数组(在本示例中为MyClass):

class MyClass {
    var a = 1
    init(a: Int)
    {
        self.a = a
    }
}

extension UIView
{
    static var extraData = [UIView: MyClass]()

    var myClassData: MyClass? {
        get {
            return UIView.extraData[self]
        }
        set(value) {
            UIView.extraData[self] = value
        }
    }
}

// Test Code: (Ran in a Swift Playground)
var view1 = UIView()
var view2 = UIView()

view1.myClassData = MyClass(a: 1)
view2.myClassData = MyClass(a: 2)
print(view1.myClassData?.a)
print(view2.myClassData?.a)

你有什么主意如何自动清除extraData,以免引起内存泄漏?
DoubleK 2016年

我实际上并没有在视图中使用它,就我而言,我只实例化了我正在使用的类中的有限个对象,因此我并没有真正考虑过内存泄漏。我想不出一种自动的方法,但是我想在上面的设置器中,如果value == nil,您可以添加逻辑以删除元素,然后在不再需要该对象时将值设置为nil,或者也许在didReceiveMemoryWarning中。

Tnx @Dan,我使用viewWillDisapper将value设置为nil,并且工作正常,现在调用了deinit,一切正常。Tnx发布了此解决方案
DoubleK

2

这是简化且更具表现力的解决方案。它适用于值和引用类型。提升方法来自@HepaKKes答案。

关联代码:

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 associated<T>(to base: AnyObject,
                key: UnsafePointer<UInt8>,
                policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN,
                initialiser: () -> T) -> T {
    if let v = objc_getAssociatedObject(base, key) as? T {
        return v
    }

    if let v = objc_getAssociatedObject(base, key) as? Lifted<T> {
        return v.value
    }

    let lifted = Lifted(initialiser())
    objc_setAssociatedObject(base, key, lifted, policy)
    return lifted.value
}

func associate<T>(to base: AnyObject, key: UnsafePointer<UInt8>, value: T, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN) {
    if let v: AnyObject = value as AnyObject? {
        objc_setAssociatedObject(base, key, v, policy)
    }
    else {
        objc_setAssociatedObject(base, key, lift(value), policy)
    }
}

用法示例:

1)创建扩展并将其关联属性。让我们同时使用值和引用类型属性。

extension UIButton {

    struct Keys {
        static fileprivate var color: UInt8 = 0
        static fileprivate var index: UInt8 = 0
    }

    var color: UIColor {
        get {
            return associated(to: self, key: &Keys.color) { .green }
        }
        set {
            associate(to: self, key: &Keys.color, value: newValue)
        }
    }

    var index: Int {
        get {
            return associated(to: self, key: &Keys.index) { -1 }
        }
        set {
            associate(to: self, key: &Keys.index, value: newValue)
        }
    }

}

2)现在您可以像常规属性一样使用:

    let button = UIButton()
    print(button.color) // UIExtendedSRGBColorSpace 0 1 0 1 == green
    button.color = .black
    print(button.color) // UIExtendedGrayColorSpace 0 1 == black

    print(button.index) // -1
    button.index = 3
    print(button.index) // 3

更多细节:

  1. 包装值类型需要提升。
  2. 默认的关联对象行为是保留。如果您想了解有关关联对象的更多信息,建议您阅读本文

1

使用Objective-C关联对象和Swift 3Swift 4计算属性的另一个示例

import CoreLocation

extension CLLocation {

    private struct AssociatedKeys {
        static var originAddress = "originAddress"
        static var destinationAddress = "destinationAddress"
    }

    var originAddress: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.originAddress) as? String
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.originAddress,
                    newValue as NSString?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }

    var destinationAddress: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.destinationAddress) as? String
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.destinationAddress,
                    newValue as NSString?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }

}

1

如果您想为UIView设置自定义字符串属性,这就是我在Swift 4上所做的

创建一个UIView扩展

extension UIView {

    func setStringValue(value: String, key: String) {
        layer.setValue(value, forKey: key)
    }

    func stringValueFor(key: String) -> String? {
        return layer.value(forKey: key) as? String
    }
}

使用此扩展

let key = "COLOR"

let redView = UIView() 

// To set
redView.setStringAttribute(value: "Red", key: key)

// To read
print(redView.stringValueFor(key: key)) // Optional("Red")

1

如何将静态地图存储到这样扩展的类中:

extension UIView {

    struct Holder {
        static var _padding:[UIView:UIEdgeInsets] = [:]
    }

    var padding : UIEdgeInsets {
        get{ return UIView.Holder._padding[self] ?? .zero}
        set { UIView.Holder._padding[self] = newValue }
    }

}

1
为什么这个答案没有投票。毕竟是智能解决方案。
byJeevan

这工作,但我觉得这里说这种方法是有缺陷的medium.com/@valv0/...
苔菲

0

我试图通过使用objc_getAssociatedObject,objc_setAssociatedObject来存储属性,但没有任何运气。我的目标是为UITextField创建扩展名,以验证文本输入字符的长度。以下代码对我来说很好用。希望这会帮助某人。

private var _min: Int?
private var _max: Int?

extension UITextField {    
    @IBInspectable var minLength: Int {
        get {
            return _min ?? 0
        }
        set {
            _min = newValue
        }
    }

    @IBInspectable var maxLength: Int {
        get {
            return _max ?? 1000
        }
        set {
            _max = newValue
        }
    }

    func validation() -> (valid: Bool, error: String) {
        var valid: Bool = true
        var error: String = ""
        guard let text = self.text else { return (true, "") }

        if text.characters.count < minLength {
            valid = false
            error = "Textfield should contain at least \(minLength) characters"
        }

        if text.characters.count > maxLength {
            valid = false
            error = "Textfield should not contain more then \(maxLength) characters"
        }

        if (text.characters.count < minLength) && (text.characters.count > maxLength) {
            valid = false
            error = "Textfield should contain at least \(minLength) characters\n"
            error = "Textfield should not contain more then \(maxLength) characters"
        }

        return (valid, error)
    }
}

全局私有变量?您是否意识到_min_max是全局的,并且在UITextField的所有实例中都相同?即使对您有用,此答案也无关紧要,因为Marcos询问实例变量。
凯林

是的,您说对了,这不适用于所有实例。我通过将最小值和最大值存储在struct中进行修复。对不起主题。
Vadims Krutovs '16

0

这是一个可行的替代方案

public final class Storage : AnyObject {

    var object:Any?

    public init(_ object:Any) {
        self.object = object
    }
}

extension Date {

    private static let associationMap = NSMapTable<NSString, AnyObject>()
    private struct Keys {
        static var Locale:NSString = "locale"
    }

    public var locale:Locale? {
        get {

            if let storage = Date.associationMap.object(forKey: Keys.Locale) {
                return (storage as! Storage).object as? Locale
            }
            return nil
        }
        set {
            if newValue != nil {
                Date.associationMap.setObject(Storage(newValue), forKey: Keys.Locale)
            }
        }
    }
}



var date = Date()
date.locale = Locale(identifier: "pt_BR")
print( date.locale )

-1

我发现此解决方案更实用

Swift 3更新

extension UIColor {

    static let graySpace = UIColor.init(red: 50/255, green: 50/255, blue: 50/255, alpha: 1.0)
    static let redBlood = UIColor.init(red: 102/255, green: 0/255, blue: 0/255, alpha: 1.0)
    static let redOrange = UIColor.init(red: 204/255, green: 17/255, blue: 0/255, alpha: 1.0)

    func alpha(value : CGFloat) -> UIColor {
        var r = CGFloat(0), g = CGFloat(0), b = CGFloat(0), a = CGFloat(0)
        self.getRed(&r, green: &g, blue: &b, alpha: &a)
        return UIColor(red: r, green: g, blue: b, alpha: value)
    }

}

...然后在您的代码中

class gameController: UIViewController {

    @IBOutlet var game: gameClass!

    override func viewDidLoad() {
        self.view.backgroundColor = UIColor.graySpace

    }
}

这是正确的方法-但实际上并不是像问题所要求的那样在扩展中存储属性。
UKDataGeek '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.