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?
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With