Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to instantiate class and init from string in Swift?

I could instantiate class from string in ObjC.For example,I have defined new DBCell class through subclassing UITableViewCell,and I instantiate class from name with these codes:

 DBCell *cell=[tableView dequeueReusableCellWithIdentifier:cellClassname];
    if (cell==nil) {
        Class cellClass=NSClassFromString(cellClassname);
        cell=[cellClass alloc];
        cell=[cell initWithStyle:cellStyle reuseIdentifier:cellClassname];
    }

Now I need migrate codes to Swift,I have redefined DBCell class in Swift:

 class DBCell: UITableViewCell {
    var label:String?
    var key:String?
    var managedObject:NSManagedObject?
    var delegate:AnyObject?
    convenience override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        self.init(style: style, reuseIdentifier: reuseIdentifier)
        self.textLabel.font=UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1)
        self.backgroundColor=UIColor.blackColor()
        self.textLabel.backgroundColor=UIColor.blackColor()
    }
    }

But how can I instantiate class and invoke the appropriate init function?

like image 270
user1190178 Avatar asked Sep 16 '14 08:09

user1190178


People also ask

How do you instantiate a class in Swift?

Instantiating an instance of a class is very similar to invoking a function. To create an instance, the name of the class is followed by a pair of parentheses, and the return value is assigned to a constant or variable.

What is super init () Swift?

super is just reference to superclass and that superclass have init method, so by calling super.init() you call init method of superclass without parameters. If init method of superclass have parameters class Animal { init(name: String) { } } you must pass parameters to this method class Cat: Animal { init() { super.

What is convenience init in Swift?

Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer's parameters set to default values.


1 Answers

Swift is more statically typed, which makes it much safer at runtime. For instance, if any of the strings are wrong, you'll crash (or at best quietly do nothing, which can be worse than crashing) and the compiler can't catch it for you.

OK, but how do you deal with this pattern? The best way, IMO, in Swift is to be explicit and create a mapping.

let cellMapping = [
  "DBCell": DBCell.self,
]

if let cellClass = cellMapping[cellClassName] {
  let cell = cellClass(style: cellStyle, reuseIdentifier: cellClassName)
  // use cell
}

Now when you refactor and change the name of DBCell, but miss the string in the Storyboard, everything will just keep working as expected, rather than crashing or quietly failing like it would in ObjC.

Swift is really smart about these mappings, too. The type of cellMapping above is [String: DBCell.Type], so if you had a special init for DBCell, it would be available. But if your dictionary looked like:

let cellMapping = [
  "DBCell": DBCell.self,
  "OtherCell": UITableViewCell.self
]

then the type is [String: UITableViewCell.Type]. Swift automatically figures out the most specific class that covers all the values. If you added a type into this list that didn't have some method you actually use, Swift can catch that at compile time. And if you made a mistake and added something that isn't even a table view cell:

let cellMapping = [
  "DBCell": DBCell.self,
  "OtherCell": UITableViewCell.self,
  "BadCell": UICollectionViewCell.self
]

then Swift will give you a compile time warning that you've created an AnyObject. That's really nice in writing safe but flexible code, all for the cost of a dictionary.

like image 190
Rob Napier Avatar answered Oct 06 '22 03:10

Rob Napier