【问题标题】:How to override NSDate description? ..Method Swizzling?如何覆盖 NSDate 描述? ..方法混乱?
【发布时间】:2016-02-02 17:15:31
【问题描述】:

我怎么打电话

print(NSDate()) 

而不是接收通常的响应,而是在名为getString() 的函数中获取我拥有的响应,该函数是NSDateextension 的一部分。

这是我的扩展:

extension NSDate {
    
   //NSDate to String
    public func getString() -> String {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()
        
        return dateFormatter.stringFromDate(self)
    }
}

请注意,我不想只使用:

NSDate().getString()

我想覆盖这个类的原始description

更新:

所以,看起来唯一的选择就是Method Swizzling如果 甚至可能

有人对赏金感兴趣吗?

注意

我这样做只是为了个人成长和了解 概念,不打算在这种情况下使用它来发布应用程序, 现在甚至不确定我可以在什么情况下使用它。

【问题讨论】:

  • 显示你的分机NSDate().getString() ?
  • 我认为没有必要,除了这个功能我没有添加任何其他东西。我尝试覆盖描述 var 或 func,但我收到一个错误,抱怨 obj 选择器进行描述。不过,我会用它来更新我的答案
  • 这就是重点,我不想这样。我想换个说法。
  • 我不认为你可以重写NSDatedescription 方法,至少不容易。您不能覆盖扩展中的方法,只能覆盖子类中的方法。您可以将其替换为方法调配。这两种方式都很困难(如果可能的话),因为 NSDate 是类集群的一部分。
  • @HugoAlonso:我的建议是:不要这样做 :) – 说真的,我没有解决方案。关于子类化,请参见例如stackoverflow.com/questions/2657275/how-do-i-subclass-nsdate.

标签: ios swift swift2 nsdate method-swizzling


【解决方案1】:
import Foundation

extension NSDate: Streamable {
    public func writeTo<Target : OutputStreamType>(inout target: Target) {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()

        print("without swizzling", dateFormatter.stringFromDate(self), toStream: &target)
    }
}

let date = NSDate()

print(date) // without swizzling 2016-03-05 00:09:34 +0100

打印“默认”/原始行为/使用

print(date.description)

如果您担心在扩展程序中使用 print,只需将其替换为

//print("without swizzling", dateFormatter.stringFromDate(self), toStream: &target)
let str = dateFormatter.stringFromDate(self)
str.writeTo(&target)

【讨论】:

  • 好东西。我从来没有考虑过 Streamable。很高兴看到是否在行动。
  • @RobNapier 关于 Streamable 的信息并不多,默认情况下只有 Character、String 和 UnicodeScalar 符合 Streamable。我发现 Streamable 对于 JSON 序列化非常有用 ...
  • 这种实现有什么缺点吗?
【解决方案2】:

我很确定这是一个可怕的、糟糕的、不好的、可怕的想法。但是给你:

extension NSDate {
    private static let dateFormatter = NSDateFormatter()
    private static var once = dispatch_once_t()
    static func swizzleDescription() {
        dispatch_once(&once) {
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
            dateFormatter.locale = NSLocale.currentLocale()

            let originalMethod = class_getInstanceMethod(self, "description")
            let replacementMethod = class_getInstanceMethod(self, "description_terribleIdea")
            method_exchangeImplementations(originalMethod, replacementMethod)
        }
    }

    func description_terribleIdea() -> String {
        return NSDate.dateFormatter.stringFromDate(self)
    }
}

let date = NSDate()
print(date)
NSDate.swizzleDescription()
print(date)

输出:

2016-03-04 22:17:20 +0000
2016-03-04 16:17:20 -0600

【讨论】:

  • 我这样做只是为了个人成长和理解这个概念,而不是计划使用它来发布应用程序。谢谢,我将在下周一对其进行审查和测试。
  • 赞成这是一个可怕的想法。 :)
  • @RobNapier 这不是那么可怕的想法,但它不适用于结构、非 objc 类等。解决方案存在!只需实现 Streamable 协议即可。请看我的回答。
【解决方案3】:

可以定义您自己的打印版本:

func print(date: NSDate) {
    print(date.getString())
}

extension NSDate {

    //NSDate to String
    public func getString() -> String {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()

        return dateFormatter.stringFromDate(self)
    }
}

这两个调用现在都将打印相同的内容:

let date = NSDate()
print(date.getString())
print(date)

【讨论】:

  • 问题是,它只有在使用带有 NSDate 类型参数的 print 时才有效。试试 print(NSDate(), NSDate()) 看看结果……或者干脆 print("now is: ", NSDate()) 等等。
  • 是的,但它确实回答了这个问题;)
【解决方案4】:

您可以在模块中使用相同的类名定义 NSDate 的覆盖。您必须遵循覆盖 NSDate 的指南,这有点乏味(但至少在操场上可以使用)

编译器应该在作用域内使用你的覆盖而不是基础覆盖:

 public class NSDate:Foundation.NSDate
 {
     override public var description:String { return getString() }

     private var dateValue:NSTimeInterval = Foundation.NSDate().timeIntervalSinceReferenceDate

     override public var timeIntervalSinceReferenceDate:NSTimeInterval { return dateValue }

     override public init()
     { super.init() }

     override public init(timeIntervalSinceReferenceDate ti: NSTimeInterval)
     { 
       super.init()
       dateValue = ti 
     }

     required public init?(coder aDecoder: NSCoder)
     { super.init(coder:aDecoder) }

     //NSDate to String
     public func getString() -> String 
     {
         let dateFormatter = NSDateFormatter()
         dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
         dateFormatter.locale = NSLocale.currentLocale()
         return dateFormatter.stringFromDate(self) + " This is the Overriden One"
     }
 }

 print("\(NSDate())")

打印:2016-03-04 18:16:22 -0500 这是被覆盖的人

请注意,这实际上覆盖了描述变量,而不仅仅是欺骗打印函数。

【讨论】:

  • 那是因为我有先见之明 :) 不,实际上我指的是用 writeTo 扩展 NSDate 的答案,它基本上做同样的事情(即仅适用于打印)
  • 也许“trick”这个词的选择很糟糕。我的意思是,该方法的范围仅限于“打印”(或更一般地“输出”),而不是实际覆盖描述变量,这就是我认为 OP 的表述方式。
  • 子类化是您覆盖类的变量和函数的方式。扩展是一个不同的概念。
【解决方案5】:

根据定义,扩展可以添加功能但不能覆盖它们。 源码:The Swift Programming Language - Extensions

您可以做的是实现协议 CustomStringConvertible 并替换该行为:

extension NSDate : CustomStringConvertible {
   var description: String {
     let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()

        return dateFormatter.stringFromDate(self)
  }
}

【讨论】:

  • 那不会编译,并且你不能替换/覆盖扩展(同一个类)中的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
相关资源
最近更新 更多