自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Swift面向協(xié)議編程(POP)的一些Tips

移動(dòng)開(kāi)發(fā)
協(xié)議最常見(jiàn)的用法莫過(guò)于進(jìn)行代理傳值,這就是委托模式。常用的應(yīng)用場(chǎng)景有:controller中自定義了一個(gè)view,view中又添加了一個(gè)自定義view。在自定義的view中如果有些函數(shù)或者屬性需要到controller中去調(diào)用,委托模式的做法就是規(guī)定一個(gè)協(xié)議,讓controller去遵守一個(gè)協(xié)議并提供實(shí)現(xiàn)。

一、委托模式

1、使用過(guò)程

協(xié)議最常見(jiàn)的用法莫過(guò)于進(jìn)行代理傳值,這就是委托模式。常用的應(yīng)用場(chǎng)景有:controller中自定義了一個(gè)view,view中又添加了一個(gè)自定義view。在自定義的view中如果有些函數(shù)或者屬性需要到controller中去調(diào)用,委托模式的做法就是規(guī)定一個(gè)協(xié)議,讓controller去遵守一個(gè)協(xié)議并提供實(shí)現(xiàn),那么在自定義view中就能使用協(xié)議中的方法。

Swift面向協(xié)議編程(POP)的一些Tips

舉個(gè)例子,現(xiàn)在想在一個(gè)controller中添加一個(gè)自定義view,可以實(shí)現(xiàn)點(diǎn)擊view中按鈕更改controller中l(wèi)abel的值。簡(jiǎn)單的代碼如下:

自定義view 

  1. //SelectTabbar.swift 
  2. @objc protocol SelectTabbarDelegate { 
  3.     func changeLabel(_ str: String) 
  4.  
  1. //SelectTabbar.swift 
  2.  class SelectTabbar: UIView { 
  3.     var keywords : [String]? 
  4.     var buttons : [UIButton]? 
  5.     weak public var delegate : SelectTabbarDelegate? 
  6.       
  7.     init(frame: CGRect,keywords:[String]) { 
  8.         super.init(frame: frame) 
  9.         self.keywords = keywords 
  10.         renderView() 
  11.     } 
  12.       
  13.     required init?(coder aDecoder: NSCoder) { 
  14.         fatalError("init(coder:) has not been implemented"
  15.     } 
  16.       
  17.     override func layoutSubviews() { 
  18.         super.layoutSubviews() 
  19.     } 
  20.       
  21.     private func renderView(){ 
  22.         buttons = keywords?.enumerated().map({ (index,key) ->UIButton in 
  23.             let buttonWidth = kScreenWidth/CGFloat((keywords?.count)!) 
  24.             let button = UIButton.init(frame: CGRect.init(x: CGFloat(index)*buttonWidth, y: 0, width: buttonWidth, height: 50)) 
  25.             button.setTitle(keyfor: .normal) 
  26.             button.setTitleColor(UIColor.blue, for: .normal) 
  27.             button.backgroundColor = UIColor.gray 
  28.             button.tag = index 
  29.             button.addTarget(self, action: #selector(tapButton(sender:)), for: .touchUpInside) 
  30.             addSubview(button) 
  31.             return button 
  32.         }) 
  33.     } 
  34.       
  35.     @objc func tapButton(sender: UIButton){ 
  36.         delegate?.changeLabel(keywords![sender.tag]) 
  37.     } 
  38.       

controller: 

  1. class TestViewController: UIViewController,SelectTabbarDelegate { 
  2.     lazy var label : UILabel = { 
  3.         var label = UILabel(frame: CGRect.init(x: 50, y: 200, width: 100, height: 30)) 
  4.         label.text = labelStr 
  5.         label.backgroundColor = UIColor.red 
  6.         return label 
  7.     }() 
  8.       
  9.     private var labelStr : String? { 
  10.         didSet{ 
  11.             label.text = labelStr 
  12.         } 
  13.     } 
  14.       
  15.     override func viewDidLoad() { 
  16.         super.viewDidLoad() 
  17.         view.backgroundColor = .white 
  18.         view.addSubview(label) 
  19.         setupSelectTabbar() 
  20.     } 
  21.       
  22.     func setupSelectTabbar(){ 
  23.         let selectTabbar = SelectTabbar(frame: CGRect.init(x: 0, y: kNavigationHeightAndStatuBarHeight, width: kScreenWidth, height: 50),keywords:["aa","bb"]) 
  24.         selectTabbar.delegate = self 
  25.         view.addSubview(selectTabbar) 
  26.     } 
  27.     func changeLabel(_ str: String) { 
  28.         labelStr = str 
  29.     } 
  30.       

這樣就能比較清楚的表明自己的邏輯。否則,如果要在view操作controller的內(nèi)容,則需要在外部操作controller的實(shí)例,這就造成一個(gè)問(wèn)題,就是無(wú)法操作實(shí)例中的私有屬性和私有方法(雖然iOS是一門動(dòng)態(tài)語(yǔ)言,不存在絕對(duì)的私有,但是誰(shuí)會(huì)去一直去使用runtime來(lái)進(jìn)行操作呢)。

2、注意點(diǎn)

在 ARC 中,對(duì)于一般的 delegate,我們會(huì)在聲明中將其指定為 weak,在這個(gè) delegate 實(shí)際的對(duì)象被釋放的時(shí)候,會(huì)被重置回 nil。這可以保證即使 delegate 已經(jīng)不存在時(shí),我們也不會(huì)由于訪問(wèn)到已被回收的內(nèi)存而導(dǎo)致崩潰。ARC 的這個(gè)特性杜絕了 Cocoa 開(kāi)發(fā)中一種非常常見(jiàn)的崩潰錯(cuò)誤,說(shuō)是救萬(wàn)千程序員于水火之中也毫不為過(guò)。

在 Swift 中我們當(dāng)然也會(huì)希望這么做。但是當(dāng)我們嘗試書(shū)寫(xiě)這樣的代碼的時(shí)候,編譯器不會(huì)讓我們通過(guò):

  1. 'weak' cannot be applied to non-class type 

原因:這是因?yàn)?Swift 的 protocol 是可以被除了 class 以外的其他類型遵守的,而對(duì)于像 struct 或是 enum 這樣的類型,本身就不通過(guò)引用計(jì)數(shù)來(lái)管理內(nèi)存,所以也不可能用 weak 這樣的 ARC 的概念來(lái)進(jìn)行修飾。

兩種解決方法:

  1. 使用@objc
  2. 聲明類類型專屬協(xié)議。通過(guò)添加 class 關(guān)鍵字來(lái)限制協(xié)議只能被類類型遵循,而結(jié)構(gòu)體或枚舉不能遵循該協(xié)議。class 關(guān)鍵字必須***個(gè)出現(xiàn)在協(xié)議的繼承列表中,在其他繼承的協(xié)議之前
  • protocol SelectTabbarDelegate : class

二、AOP編程思想的運(yùn)用

首先我們理解下AOP的含義。

  • In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a “pointcut” specification, such as “log all function calls when the function’s name begins with ‘set’”. This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code, core to the functionality. AOP forms a basis for aspect-oriented software development.

在swift簡(jiǎn)單來(lái)說(shuō),就是利用協(xié)議去切入某些代碼中,將額外的功能單獨(dú)出來(lái)而不產(chǎn)生耦合,可以將這些與主邏輯關(guān)系不大的代碼統(tǒng)一放到一起。

常用的場(chǎng)景:日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等等。

接上面的例子,我們需要在打開(kāi)TestViewController的時(shí)候統(tǒng)計(jì)一次,點(diǎn)擊兩個(gè)按鈕的時(shí)候也進(jìn)行統(tǒng)計(jì),統(tǒng)計(jì)的內(nèi)容由identifer進(jìn)行區(qū)分。

我們先建立一個(gè)Statistician.swift 來(lái)存放我們的統(tǒng)計(jì)邏輯。(模擬實(shí)現(xiàn))

申明一個(gè)StatisticianProtocal協(xié)議并提供他的默認(rèn)實(shí)現(xiàn)。 

  1. import Foundation 
  2. enum LogIdentifer:String { 
  3.     case button1 = "button1" 
  4.     case button2 = "button2" 
  5.     case testViewController = "testViewController" 
  6.   
  7. protocol StatisticianProtocal { 
  8.     func statisticianLog(fromClass:AnyObject, identifer:LogIdentifer) 
  9.     func statisticianUpload(fromClass:AnyObject, identifer:LogIdentifer) 
  10.     //用一個(gè)尾隨閉包來(lái)擴(kuò)展功能 
  11.     func statisticianExtension(fromClass:AnyObject, identifer:LogIdentifer, extra:()->()) 
  12.   
  13. extension StatisticianProtocal{ 
  14.     func statisticianLog(fromClass:AnyObject, identifer:LogIdentifer) { 
  15.         print("statisticianLog--class:\(fromClass) from:\(identifer.rawValue)"
  16.     } 
  17.       
  18.     func statisticianUpload(fromClass:AnyObject, identifer:LogIdentifer) { 
  19.         print("statisticianUpload--class:\(fromClass) from:\(identifer.rawValue)"
  20.     } 
  21.       
  22.     func statisticianExtension(fromClass:AnyObject, identifer:LogIdentifer, extra:()->()){ 
  23.         extra() 
  24.     } 
  25.   
  26. class Statistician: NSObject { 
  27.   

接下來(lái)在任何需要統(tǒng)計(jì)的類里面,我們讓這個(gè)類去遵守這個(gè)協(xié)議,然后在需要的地方調(diào)用協(xié)議中的方法即可。如果在某個(gè)特定的類中需要調(diào)用的方法略有不同,重寫(xiě)協(xié)議中的方法即可。 

  1. class SelectTabbar: UIView,StatisticianProtocal { 
  2.     var keywords : [String]? 
  3.     var buttons : [UIButton]? 
  4.     weak public var delegate : SelectTabbarDelegate? 
  5.       
  6.     init(frame: CGRect,keywords:[String]) { 
  7.         super.init(frame: frame) 
  8.         self.keywords = keywords 
  9.         renderView() 
  10.         //進(jìn)行一次統(tǒng)計(jì) 
  11.         operateStatistician(identifer: .testViewController) 
  12.     } 
  13.       
  14.     required init?(coder aDecoder: NSCoder) { 
  15.         fatalError("init(coder:) has not been implemented"
  16.     } 
  17.       
  18.       
  19.     override func layoutSubviews() { 
  20.         super.layoutSubviews() 
  21.     } 
  22.       
  23.     private func renderView(){ 
  24.         buttons = keywords?.enumerated().map({ (index,key) ->UIButton in 
  25.             let buttonWidth = kScreenWidth/CGFloat((keywords?.count)!) 
  26.             let button = UIButton.init(frame: CGRect.init(x: CGFloat(index)*buttonWidth, y: 0, width: buttonWidth, height: 50)) 
  27.             button.setTitle(keyfor: .normal) 
  28.             button.setTitleColor(UIColor.blue, for: .normal) 
  29.             button.backgroundColor = UIColor.gray 
  30.             button.tag = index 
  31.             button.addTarget(self, action: #selector(tapButton(sender:)), for: .touchUpInside) 
  32.             addSubview(button) 
  33.             return button 
  34.         }) 
  35.     } 
  36.       
  37.     @objc func tapButton(sender: UIButton){ 
  38.         //進(jìn)行一次統(tǒng)計(jì) 
  39.         switch sender.tag { 
  40.           case 0:operateStatistician(identifer: .button1) 
  41.           default:operateStatistician(identifer: .button2) 
  42.         } 
  43.         delegate?.changeLabel(keywords![sender.tag]) 
  44.     } 
  45.       
  46.     func operateStatistician(identifer:LogIdentifer){ 
  47.         statisticianLog(fromClass: self, identifer: identifer) 
  48.         statisticianUpload(fromClass: self, identifer: identifer) 
  49.         statisticianExtension(fromClass: self, identifer: identifer) { 
  50.             print("extra: in SelectTabbar class"
  51.         } 
  52.     } 
  53.       

以上代碼實(shí)現(xiàn)了三處統(tǒng)計(jì)的邏輯,而不用把統(tǒng)計(jì)的邏輯寫(xiě)入controller文件中,降低了功能上的耦合度。

三、用來(lái)代替extension,增強(qiáng)代碼可讀性

使用擴(kuò)展,可以很方便的為一些繼承它的子類增添一些函數(shù)。這就帶來(lái)一個(gè)問(wèn)題,就是所有的子類都擁有了這個(gè)方法,但是方法的本身可能不明確,或者是只是想讓少數(shù)子類來(lái)使用這個(gè)方法。這時(shí)候可以使用協(xié)議來(lái)代替extension。 

  1. //定義了一個(gè)Shakable協(xié)議,遵守這個(gè)協(xié)議的類即可使用里面的方法,并為該方法提供一個(gè)默認(rèn)的實(shí)現(xiàn) 
  2. //where Self:UIView表明了只有uiview的子類可以遵守這個(gè)協(xié)議 
  3. protocol Shakable { 
  4.     func shakeView() 
  5.   
  6. extension Shakable where Self:UIView{ 
  7.     func shakeView(){ 
  8.         print(Self.self) 
  9.     } 

這時(shí)候可以讓某個(gè)子類來(lái)遵守協(xié)議。例如剛才上面的例子。

  1. class SelectTabbar: UIView,Shakable 

如果不在類中重新實(shí)現(xiàn)這個(gè)方法,則可以實(shí)現(xiàn)默認(rèn)的方法。這個(gè)意思表明,SelectTabbar類的子類是遵守Shakable協(xié)議的,間接等于SelectTabbar():Shakable?。這樣我們就可以愉快的讓SelectTabbar對(duì)象去使用這個(gè)方法。(Self關(guān)鍵字只能用在協(xié)議或者類中,表示當(dāng)前類,可作為返回值使用)。

一旦不想讓某個(gè)子類使用shakeView()方法,很簡(jiǎn)單,只要把class SelectTabbar: UIView,Shakable中的Shakable協(xié)議干掉即可。

其他實(shí)踐:

利用AOP去分離tableview的數(shù)據(jù)源和事件源的方法,可以單獨(dú)處理里面的邏輯,使tableview的代理方法不顯得那么冗余。

總結(jié)

關(guān)于協(xié)議,還有很多種用法。以上是目前比較常用的場(chǎng)景。日后開(kāi)發(fā)中如果發(fā)現(xiàn)協(xié)議在其他地方中有更好的應(yīng)該,將會(huì)更新本文

 

責(zé)任編輯:未麗燕 來(lái)源: peipeiblog
相關(guān)推薦

2023-10-09 08:14:10

Helm管理應(yīng)用

2013-07-02 10:18:20

編程編程策略

2013-07-02 09:43:02

編程策略

2010-06-09 17:13:12

IPv6協(xié)議路由協(xié)議

2021-10-13 07:48:23

Options模式編程

2021-04-09 10:26:43

Python編程技術(shù)

2010-08-05 13:54:36

NFS協(xié)議

2011-08-31 10:54:25

Java性能

2018-06-08 08:50:35

編程語(yǔ)言并發(fā)編程

2011-09-13 09:41:59

Python

2016-12-12 15:22:41

編程

2022-07-30 23:41:53

面向過(guò)程面向?qū)ο?/a>面向協(xié)議編程

2009-11-23 13:44:33

PHP5面向?qū)ο?/a>

2012-02-16 08:19:03

2012-04-05 13:24:30

2010-09-17 15:41:46

網(wǎng)絡(luò)協(xié)議分析軟件

2015-09-15 10:40:41

Swift2.0MVVM

2015-08-04 08:56:14

swift子類

2020-12-04 09:11:45

Python加密文件爆破字典

2015-03-30 11:21:27

編程編程反思
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)