×

使用Swift扩展的“错误”方式

消耗积分:1 | 格式:rar | 大小:0.5 MB | 2017-10-11

分享资料个

 
  翻译如下:非常感谢,这篇文章很不错。我一直疑惑你为什么要用这么多不必要的扩展,我认为你并未按照正确的用途来使用它们。能否简单地将它们当作一个整体类?
  我使用扩展的主要原因是为了增加可读性。在这些地方我喜欢使用扩展,尽管“这并不是扩展设计的初衷”。
  私有Helper函数
  在OC语言中,.h和.m文件维护起来一样烦人,从.h文件中可以很容易地看出类的外部API,而内部API则保存在.m文件中。但在Swift中,我们只有一个文件。
  因此,我们很容易看出类的公共函数:可以从外部及私有部分所访问的函数。我将私有内部函数放在私有扩展中,如下例:
  // it‘s easy to glance and see which parts of this struct are// supposed to be used externallystructTodoItemViewModel { letitem: TodoItem letindexPath: NSIndexPath vardelegate: ImageWithTextCellDelegate { returnTodoItemDelegate(item: item) } varattributedText: NSAttributedString { // the itemContent logic is in the private extension// keeping this code clean and easy to glance atreturnitemContent } } // keeps all this internal logic out of the public-facing API of this struct// MARK: Private Attributed Text Builder Helper Methodsprivateextension TodoItemViewModel { staticvarspaceBetweenInlineImages: NSAttributedString { returnNSAttributedString(string: “ ”) } varitemContent: NSAttributedString { lettext = NSMutableAttributedString(string: item.content, attributes: [NSFontAttributeName : SmoresFont.regularFontOfSize(17.0)]) ifletdueDate = item.dueDate { appendDueDate(dueDate, toText: text) } forassignee initem.assignees { appendAvatar(ofUser: assignee, toText: text) } returntext } func appendDueDate(dueDate: NSDate, toText text: NSMutableAttributedString) { ifletcalendarView = CalendarIconView.viewFromNib() { calendarView.configure(withDate: dueDate) ifletcalendarImage = UIImage.imageFromView(calendarView) { appendImage(calendarImage, toText: text) } } } func appendAvatar(ofUser user: User, toText text: NSMutableAttributedString) { ifletavatarImage = user.avatar { appendImage(avatarImage, toText: text) } else{ appendDefaultAvatar(ofUser: user, toText: text) downloadAvatarImage(forResource: user.avatarResource) } } func downloadAvatarImage(forResource resource: Resource?) { ifletresource = resource { KingfisherManager.sharedManager.retrieveImageWithResource(resource, optionsInfo: nil, progressBlock: nil) { image, error, cacheType, imageURL iniflet_ = image { dispatch_async(dispatch_get_main_queue()) { NSNotificationCenter.defaultCenter().postNotificationName(TodoItemViewModel.viewModelViewUpdatedNotification, object: self.indexPath) } } } } } func appendDefaultAvatar(ofUser user: User, toText text: NSMutableAttributedString) { ifletdefaultAvatar = user.defaultAvatar { appendImage(defaultAvatar, toText: text) } } func appendImage(image: UIImage, toText text: NSMutableAttributedString) { text.appendAttributedString(TodoItemViewModel.spaceBetweenInlineImages) letattachment = NSTextAttachment() attachment.image = image letyOffsetForImage = -7.0asCGFloat attachment.bounds = CGRectMake(0.0, yOffsetForImage, image.size.width, image.size.height) letimageString = NSAttributedString(attachment: attachment) text.appendAttributedString(imageString) } }
  注意:在上例中,属性字符串计算的逻辑超级复杂。如果存在于结构(struct)的主要部分中,我们就无法轻易发现它,并了解哪个部分比较重要了(在OC语言中这个部分是属于.h文件的)。 这样做也会使得代码更干净。
  尤其如果这个属性字符串在代码中其他地方还有调用的话,使用这样的长扩展将逻辑重构到自身结构中会是很好的开头。不过在编写代码时,将它放在私有扩展中会更好。
  分组
  一开始我在Swift中真正使用扩展的原因在于:Swift刚出来的时候,无法添加pragma mark注释。没错,Swift出现后我想用它做的第一件事就是这个。在OC代码中,我通过pragma mark来进行区分,换到Swift后还想继续用。
  于是我去了WWDC的Swift Labs,询问如何在Swift中添加pragma marks,回答者建议我使用扩展来替代。于是我开始使用扩展了,并完全爱上了这种用法。
  尽管pragma marks(在Swift中是//MARK)非常棒,但在新写的代码中很容易忘记添加MARK,对于编码风格多样的团队来说更是如此。结果就是该MARK的函数没MARK上,不该加的反而有了。因此,如果一组函数之间相互归属,我倾向于将它们放在同一个扩展中。
  一般我总会有一个扩展是将所有ViewController或者AppDelegate的初始样式设计方法归类到一起的:
  private extension AppDelegate { func configureAppStyling() { styleNavigationBar() styleBarButtons() } func styleNavigationBar() { UINavigationBar.appearance().barTintColor= ColorPalette.ThemeColorUINavigationBar.appearance().tintColor= ColorPalette.TintColorUINavigationBar.appearance().titleTextAttributes= [ NSFontAttributeName : SmoresFont.boldFontOfSize(19.0), NSForegroundColorAttributeName : UIColor.blackColor() ] } func styleBarButtons() { let barButtonTextAttributes = [ NSFontAttributeName : SmoresFont.regularFontOfSize(17.0), NSForegroundColorAttributeName : ColorPalette.TintColor] UIBarButtonItem.appearance().setTitleTextAttributes(barButtonTextAttributes, forState: .Normal) } }
  或者将所有通知逻辑归类到一起:
  extension TodoListViewController { // called in initfunc addNotificationObservers() { NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector(“onViewModelUpdate:”), name: TodoItemViewModel.viewModelViewUpdatedNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector(“onTodoItemUpdate:”), name: TodoItemDelegate.todoItemUpdatedNotification, object: nil) } func onViewModelUpdate(notification: NSNotification) { ifletindexPath = notification.objectas? NSIndexPath { tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None) } } func onTodoItemUpdate(notification: NSNotification) { ifletitemObject = notification.objectas? ValueWrapper《TodoItem》 { letupdatedItem = itemObject.valueletupdatedTodoList = dataSource.listFromUpdatedItem(updatedItem) dataSource = TodoListDataSource(todoList: updatedTodoList) } } }
  协议一致性
  This is a special case of grouping. 我喜欢将所有符合同一个协议的函数放在同一个扩展中。在OC语言中我是用pragma mark来实现的,但我更偏爱扩展的硬分隔和可读性:
  structTodoItemViewModel { staticletviewModelViewUpdatedNotification = “viewModelViewUpdatedNotification”letitem: TodoItem letindexPath: NSIndexPath vardelegate: ImageWithTextCellDelegate { returnTodoItemDelegate(item: item) } varattributedText: NSAttributedString { returnitemContent } } // ImageWithTextCellDataSource Protocol Conformance extension TodoItemViewModel: ImageWithTextCellDataSource { varimageName: String { returnitem.completed ? “checkboxChecked”: “checkbox”} varattributedText: NSAttributedString { returnitemContent } }
  用扩展对UITableViewDataSource与UITableViewDelegate进行分割也很奏效。
  // MARK: Table View Data Sourceextension TodoListViewController: UITableViewDataSource{ func numberOfSectionsInTableView(tableView: UITableView) -》 Int { returndataSource.sections.count} func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -》 Int { returndataSource.numberOfItemsInSection(section) } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -》 UITableViewCell{ let cell = tableView.dequeueReusableCellWithIdentifier(String.fromClass(ImageWithTextTableViewCell), forIndexPath: indexPath) as! ImageWithTextTableViewCell let viewModel = dataSource.viewModelForCell(atIndexPath: indexPath) cell.configure(withDataSource: viewModel, delegate: viewModel.delegate) returncell } } // MARK: Table View Delegateextension TodoListViewController: UITableViewDelegate{ // MARK: Cell Selectionfunc tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { performSegueWithIdentifier(todoItemSegueIdentifier, sender: self) } // MARK: Section Header Configurationfunc tableView(tableView: UITableView, viewForHeaderInSection section: Int) -》 UIView? { ifdataSource.sections[section] == TodoListDataSource.Section.DoneItems{ let view = UIView() view.backgroundColor= ColorPalette.SectionSeparatorColorreturnview } returnnil} func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -》 CGFloat{ ifdataSource.sections[section] == TodoListDataSource.Section.DoneItems{ return1.0} return0.0} // MARK: Deleting Actionfunc tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -》 Bool { returntrue} func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -》 [UITableViewRowAction]? { let deleteAction = UITableViewRowAction(style: .Destructive, title: “Delete”) { [weakself] action , indexPath in iflet updatedTodoList = self?.dataSource.listFromDeletedIndexPath(indexPath) { self?.dataSource= TodoListDataSource(todoList: updatedTodoList) } } return[deleteAction] } }
  模型
  我在OC中使用Core Data时非常喜欢使用模型,因为在更改时模型由Xcode生成,因此需要将函数或任何其他内容放入同一个扩展或分类中。
  我尝试尽可能在Swift中使用结构,但还是更喜欢用扩展来分出模型属性与基于属性的计算。对我来说,这样增加了模型代码的可读性。
  总结
  尽管这种做法也许不够“传统”,在Swift中对扩展的超简单运用能极大程度地改善代码,让代码超级具有可读性。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !