仅公开便利init的子类化

时间:2015-06-04 01:43:33

标签: xcode swift

我想子类化声明如下的UITableViewRowAction类:

class UITableViewRowAction : NSObject, NSCopying {

  convenience init(style: UITableViewRowActionStyle, title: String!, handler: (UITableViewRowAction!, NSIndexPath!) -> Void)

  var style: UITableViewRowActionStyle { get }
  var title: String!
  @NSCopying var backgroundColor: UIColor! // default background color is dependent on style
  @NSCopying var backgroundEffect: UIVisualEffect?
}

我希望我的子类覆盖此处存在的方便init,并调用super来获得方便性。

以下是我想要做的事情:

public class MySubclass : UITableViewRowAction  {

  let handler : (UITableViewRowAction!, NSIndexPath!) -> Void
  public init(style: UITableViewRowActionStyle, title: String!, handler: (UITableViewRowAction!, NSIndexPath!) -> Void) {
      self.handler = handler
      super.init(style: style, title: title, handler: handler)  // error == Must call a designated initializer of the superclass 'UITableViewRowAction'
  }
}

我想这样做是为了将处理程序公开为readonly属性,以便能够对处理程序执行我认为应该执行的操作进行单元测试。

所以我的问题是超级类没有公开传递给init方法的处理程序,并且没有办法在init方法之外设置它。

3 个答案:

答案 0 :(得分:2)

我不知道这个答案是否真的与你的答案完全不同,但我在尝试进行单元测试时遇到了类似的问题。这个感觉有点清洁;但这是主观的。您可以使用set_AssociatedObject(和get_AssociatedObject)和一个小的私有ActionTrigger包装类来允许在单元测试中触发操作。

import UIKit
import ObjectiveC

var ActionTriggerHandle: UInt8 = 0

private class ActionTrigger {
    private var handler: (UITableViewRowAction!, NSIndexPath!) -> ()
    private var action: UITableViewRowAction

    private init(action: UITableViewRowAction, handler: (UITableViewRowAction!, NSIndexPath!) -> Void) {
        self.handler = handler
        self.action = action
    }

    private func trigger(indexPath: NSIndexPath) {
        handler(action, indexPath)
    }
}

public extension  UITableViewRowAction  {
    public convenience init(title: String!, style: UITableViewRowActionStyle, exposedHandler: (UITableViewRowAction!, NSIndexPath!) -> Void) {
        self.init(style: style, title: title, handler: exposedHandler)
        var actionTrigger = ActionTrigger(action: self, handler: exposedHandler)
        objc_setAssociatedObject(self, &ActionTriggerHandle, actionTrigger, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
    }

    public func trigger(indexPath:NSIndexPath) {
        if let storedActionTrigger = objc_getAssociatedObject(self, &ActionTriggerHandle) as? ActionTrigger {
            storedActionTrigger.trigger(indexPath)
        }
    }
}

答案 1 :(得分:0)

您无法继承UITableViewRowAction。将其视为final类。 (Objective-C没有正式的方式来声明最终的类,但是,非正式地,由于缺少公开声明的初始化程序,这个类在最终中。)

如果查看原始的Obj-C代码,UITableViewRowAction实例只能使用类方法创建:

+ (instancetype)rowActionWithStyle:(UITableViewRowActionStyle)style
                             title:(NSString *)title
                           handler:(void (^)(UITableViewRowAction *action, NSIndexPath *indexPath))handler;

(那个类方法被转换为你在Swift中看到的便利初始化器。)

UITableViewRowAction未声明任何初始值设定项。此外,它的style属性是只读的,不能在上面的类方法之外分配。

无法在此类方法之外实例化UITableViewRowAction,因此无法继承UITableViewRowAction

答案 2 :(得分:0)

我发现一个丑陋的走动(代码需要清理)。

四处走动涉及UITableViewRowAction的扩展。 不幸的是,没有存储可以添加到实例中。 因此,添加静态数组以为创建的不同实例添加存储。 但这引发了一个保留周期的问题,这个问题在这里被使用" WeakWrapper"打破了。

我愿意接受更好的建议。

import UIKit
import FoundationAdditions

public extension  UITableViewRowAction  {

    private static var storedHandlers = [handlerStorage]()
    var handler : ((UITableViewRowAction!, NSIndexPath!) -> Void)! {
        get {
            var needCleanUp = false
            var result = {(a: UITableViewRowAction!, b: NSIndexPath!) -> Void in }
            for storage in UITableViewRowAction.storedHandlers {
                if let x = storage.instance.item {
                    if x === self {
                        result = storage.handler
                        break
                    }
                } else {
                    needCleanUp = true
                }
            }

            if needCleanUp {
                //  DO the CLEAN UP
            }

            return result
        }
    }

    public convenience init(style: UITableViewRowActionStyle, title: String!, storedHandler: (UITableViewRowAction!, NSIndexPath!) -> Void) {
        self.init(style: style, title: title, handler: storedHandler)

        let x = handlerStorage(instance: self, handler: storedHandler)
        UITableViewRowAction.storedHandlers.append(x)
    }
}

private class handlerStorage {
    let instance : WeakWrapper<UITableViewRowAction>
    let handler : ((UITableViewRowAction!, NSIndexPath!) -> Void)

    init(instance: UITableViewRowAction, handler: (UITableViewRowAction!, NSIndexPath!) -> Void) {
        self.instance = WeakWrapper<UITableViewRowAction>(item: instance)
        self.handler = handler
    }
}