1
votes

I tried to use a class which is created from NSClassFromString to perform a class method, but I failed.

In Objective-C, it's easy:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:[NSClassFromString(reuseIdentifier) cellStyle] reuseIdentifier:reuseIdentifier];
    if (self) {
         let sel = #selector(CTFeedbackTopicCellItem.cellStyle())
        // Initialization code
    }
    return self;
}

And in Swift it's not easy:

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    if let reuseIdentifier2 = reuseIdentifier
    {
        if let class2 = NSClassFromString(reuseIdentifier2) as? NSObjectProtocol
        {
            let sel = #selector(CTFeedbackCellItem.cellStyle())

            if let class3 = class2 as? CTFeedbackCellItem
            {
                super.init(style: class3.cellStyle, reuseIdentifier: reuseIdentifier2)
            }
        }
    }
}

The error shown in my Swift code so far are:
1. Argument of '#selector' does not refer to an '@objc' method, property or initializer.
2. Stastic member 'cellType' cannnot be used on instance of type 'CTFeedbackCellItem'

1
It seems that your solution is overly complicated. Why do you do it like that? Can't you use protocols/inheritance to achieve the same thing?Losiowaty
I am converting a open source to Swift. The classes created from reuseIdentifier have different cellType, so it did like this way. The simpler way I see here is only like this: ````if let class2 = NSClassFromString(reuseIdentifier2) as? CTFeedbackCellItem``` But compiler says "Cast from AnyObject to unrelated type ''CTFeedbackCellItem' alwyas fails". Can you make it simpler in any way?allenlinli
Since you are converting the open source, why not take the opportunity to fix their mistake and do it properlyPaulw11
If they all have this property cellType than it is a clear case for a protocol :) Anyway, this whole initializer looks weird. If you are already initializing this class, can't you just take its cellType? Can you link to the library you are converting?Losiowaty
There are 5 subclasses of CTFeedbackCellItem there, and they got cellType like UITableViewCellStyle.default or UITableViewCellStyle.value1. It can be a protocol, yes. The source code is here, github.com/rizumita/CTFeedback/tree/master/Classes And the answer by @OOPer seems to work.allenlinli

1 Answers

2
votes

Try this:

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    guard let reuseIdentifier = reuseIdentifier else {
        fatalError("bad usage")
    }
    guard let itemClass = NSClassFromString(reuseIdentifier) as? CTFeedbackCellItem.Type else {
        fatalError("bad reuseIdentifier")
    }
    super.init(style: itemClass.cellStyle(), reuseIdentifier: reuseIdentifier)
}

(If you have implemented your cellStyle as a class property, change itemClass.cellStyle() to itemClass.cellStyle.)

Class object is not an instance of the class, so you should not cast it like an instance. Cast it to meta-class, if you want to call class methods. No need to use #selector.