一、委托模式1、使用process协议最常见的用法是代理传值,也就是委托模式。常用的应用场景有:在controller中自定义view,在view中添加自定义view。在自定义视图中,如果需要在controller中调用一些函数或者属性,委托模式的做法是指定一个协议,让controller遵守一个协议并提供实现,然后就可以使用协议中的方法在自定义视图中。例如,如果你想给一个控制器添加一个自定义视图,你可以点击视图中的按钮来改变控制器中标签的值。简单的代码如下:自定义view//SelectTabbar.swift@objcprotocolSelectTabbarDelegate{funcchangeLabel(_str:String)}//SelectTabbar.swift classSelectTabbar:UIView{varkeywords:[String]?varbuttons:[UIButton]?weakpublicvardelegate:SelectTabbarDelegate?init(frame:CGRect,keywords:[String]){super.init(frame:frame)self.keywords=keywordsrenderView()}requiredinit?(coderaDecoder:NSCoder){fatalError("init(coder:)hasnotbeenimplemented")}overridefunclayoutSubviews(){super.layoutSubviews()}privatefuncrenderView(){buttons=keywords?.enumerated().map({(index,key)->UIButtoninletbuttonWidth=kScreenWidth/CGFloat((keywords?.count)!)letbutton=UIButton.init(frame:CGRect.init(x:CGFloat(index)*buttonWidth,y:0,width:buttonWidth,height:50))button.setTitle(key,for:.normal)button.setTitleColor(UIColor.blue,for:.normal)button.backgroundColor=UIColor.graybutton.tag=indexbutton.addTarget(self,action:#selector(tapButton(sender:)),for:.touchUpInside)addSubview(button)returnbutton})}@objcfunctapButton(sender:UIButton){delegate?.changeLabel(keywords![sender.tag])}}控制器:classTestViewController:UIViewController,SelectTabbarDelegate{lazyvarlabel:UILabel={varlabel=UILabel(frame:CGRect.init(x:50,y):200,width:100,height:30))label.text=labelStrlabel.backgroundColor=UIColor.redreturnlabel}()privatevarlabelStr:String?{didSet{label.text=labelStr}}overridefuncviewDidLoad(){super.viewDidLoad()view.backgroundColor=.whiteview.addSubview(label)setupSelectTabbar()}funcsetupSelectTabbar(){letselectTabbar=SelectTabbar(frame:CGRect.init(x:0,y:kNavigationHeightAndStatuBarHeight,width:kScreenWidth,height:50),关键字:["aa","bb"])selectTabbar.delegate=selfview.addSubview(selectTabbar)}funcchangeLabel(_str:String){labelStr=str}}这样可以清楚的展示你的逻辑否则如果你想操作controller的内容在view中,需要对外操作controller的实例,这就造成了一个问题,就是私有属性和私有方法instance是不能操作的(iOS虽然是动态语言,没有绝对的私密性,但是谁总要用runtime来操作)。2.注意事项在ARC中,对于一般的delegate,我们会在声明中将其指定为weak,当delegate的实际对象被释放时,会重置回nil。这样可以保证即使委托不存在了,我们也不会因为访问已经回收的内存而崩溃。ARC的这一特性消除了Cocoa开发中一个非常常见的崩溃错误,毫不夸张的说,它拯救了成千上万的程序员于水火之中。在Swift中,我们当然想做同样的事情。但是当我们尝试编写这样的代码时,编译器不会让我们通过:'weak'cannotbeapplyedtonon-classtype原因:这是因为Swift的协议可以被除class之外的其他类型遵守,对于struct或It之类的东西是像enum这样的类型,不通过引用计数来管理内存,所以不可能像weak那样用ARC的概念去修改。两种解决方法:使用@objc来声明特定于类类型的协议。通过添加class关键字来限制协议只能由类类型遵守,而结构或枚举不能遵守协议。class关键字必须首先出现在协议的继承列表中,出现在其他继承的协议之前protocolSelectTabbarDelegate:class2.AOP编程思想的应用首先,我们来了解一下AOP的含义。在计算中,面向方面的编程(AOP)是一种编程范例,旨在通过允许分离横切关注点来增加模块化。它通过在不修改代码本身的情况下向现有代码添加额外的行为(建议)来实现,而不是通过“切入点”规范单独指定修改哪些代码,例如“在函数名称以'set'开头时记录所有函数调用””。这允许将不属于业务逻辑核心的行为(例如日志记录)添加到程序中,而不会使代码混乱,这是功能的核心。AOP构成了面向方面软件开发的基础。在swift中,简单来说,就是用协议切入一些代码,将附加功能隔离开来,不耦合,可以把这些和主逻辑关系不大的代码放在一起。常用场景:日志记录、性能统计、安全控制、事务处理、异常处理等。继续上面的例子,我们需要在打开TestViewController的时候统计一次,点击两个按钮的时候也要统计一次,统计的内容由标识符区分。我们首先创建一个Statistician.swift来存储我们的统计逻辑。(模拟实现)声明一个StatisticianProtocal协议并提供其默认实现。importFoundationenumLogIdentifer:String{casebutton1="button1"casebutton2="button2"casetestViewController="testViewController"}protocolStatisticianProtocal{funcstatisticianLog(fromClass:AnyObject,identifer:LogIdentifer)funcstatisticianUpload(fromClass:AnyObject,identifer:LogIdentifer)//使用尾随闭包扩展函数funcstatisticianExtension(fromClass:AnyObject,identifer:LogIdentifer,extra:()->())}extensionStatisticianProtocal{funcstatisticianLog(fromClass:AnyObject,identifer:LogIdentifer){print("statisticianLog--class:\(fromClass)from:\(identifer.rawValue)")}funcstatisticianUpload(fromClass:AnyObject,identifer:LogIdentifer){print("statisticianUpload--class:\(fromClass)from:\(identifer.rawValue)")}funcstatisticianExtension(fromClass:AnyObject,identifer:LogIdentifer,extra:()->()){extra()}}classStatistician:NSObject{}接下来在任何一个需要统计的类中,我们让这个类遵守这个协议,然后在需要的地方方法中调用协议。如果您需要在特定类中调用稍微不同的方法,只需覆盖协议中的方法即可。classSelectTabbar:UIView,StatisticianProtocal{varkeywords:[String]?varbuttons:[UIButton]?weakpublicvardelegate:SelectTabbarDelegate?init(frame:CGRect,keywords:[String]){super.init(frame:frame)self.keywords=关键字渲染视图()//进行一次统计操作Statistician(identifer:.testViewController)}requiredinit?(coderaDecoder:NSCoder){fatalError("init(coder:)hasnotbeenimplemented")}overridefunclayoutSubviews(){super.layoutSubviews()}privatefuncrenderView(){buttons=keywords?,y:0,width:buttonWidth,height:50))button.setTitle(key,for:.normal)button.setTitleColor(UIColor.blue,for:.normal)button.backgroundColor=UIColor.graybutton.tag=indexbutton。addTarget(self,action:#selector(tapButton(sender:)),for:.touchUpInside)addSubview(button)returnbutton})}@objcfunctapButton(sender:UIButton){//进行一次系统计数switchsender.tag{case0:operateStatistician(identifer:.button1)default:operateStatistician(identifer:.button2)}delegate?.changeLabel(keywords![sender.tag])}funcoperateStatistician(identifer:LogIdentifer){statisticianLog(fromClass:self),identifer:identifer)statisticianUpload(fromClass:self,identifer:identifer)statisticianExtension(fromClass:self,identifer:identifer){print("extra:inSelectTabbarclass")}}}上面代码实现了三个统计的逻辑,没有把统计逻辑写入controller文件,降低了函数的耦合度。3.用于替换扩展名,增强代码的可读性。通过使用扩展,可以方便地为继承它的一些子类添加一些功能。这就带来了一个问题,就是所有的子类都有这个方法,但是这个方法本身可能不清楚,或者只是想让几个子类使用这个方法。这时候可以用协议代替扩展名。//定义一个Shakable协议,遵守这个协议的类可以使用里面的方法,并为这个方法提供一个默认的实现//whereSelf:UIView表示只有uiview的子类可以遵守这个协议protocolShakable{funcshakeView()}extensionShakablewhereSelf:UIView{funcshakeView(){print(Self.self)}}这时候可以做一个子类来遵守约定。例如上面的例子。classSelectTabbar:UIView,Shakable如果在类中不重新实现这个方法,可以实现默认方法。这意味着SelectTabbar类的子类遵守Shakable协议,间接等于SelectTabbar():Shakable?。这样我们就可以愉快的让SelectTabbar对象使用这个方法了。(Self关键字只能用在协议或类中,表示当前类,可作为返回值)。一旦不想让子类使用shakeView()方法,就很简单了,干掉SelectTabbar类中的Shakable协议即可:UIView,Shakable。其他做法:使用AOP将tableview的数据源和事件源分离,里面的逻辑可以单独处理,这样tableview的代理方法就没有那么冗余了。总结关于协议,有很多用法。以上是目前最常用的场景。在以后的开发中,如果发现该协议在其他地方有更好的应用,将会更新本文
