译者:Christian C
来源:SDK.cn
原文:Discovering Native Swift Patterns
模式(Patterns)是你首选的代码,在使用其他语言的时候,你一定已经对它有了很深的理解。但是当一个具有独特句法和功能的新语言出现之后,你能马上了解它的模式吗?我们必须要发现这个新语言当中的模式;何时应该运用旧有的知识,以及何时应该学习新的知识。
在这篇文章中,我将会谈到Objective-C(以及其他语言)中的普遍模式,并且在Swift中找到它的模式。
介绍:我是Nick O’Neill,今天我们要学习如何发现Swift模式。
设计模式总的来说,是编程中的一个组成部分,它可以解决一个非常具体的问题。应用正是由各种各样的这些模式所组成的。
一个简单的模式可以是这样的:通过一次点击,应用就进入下一屏。而复杂一些的模式则是那些你用来获取核心数据的东西。一名优秀的编程人员,就必须要知道哪种模式可以解决哪种问题。但是这些模式并不是静止不动的,尤其是当一种新的编程语言出现的时候,例如Swift,我们就要重新审视这些模式,看看这些模式能否被运用在新的语言中。
Swift中的模式
我写过一篇名叫《That Thing in Swift》的博客,那时我还是一名Objective-C开发人员。当Swift出现的时候,我就开始考虑这个问题,将Objective-C中的模式转移到Swift中。
静态单元格
这是一个基本的静态单元格视图。
Objective-C下的表达方式
if (indexPath.section == 0) {
if(indexPath.row == 0) {
cell.textLabel.text = @"Twitter"
} else if (indexPath.row == 1) {
cell.textLabel.text = @"Blog"
} else {
cell.textLabel.text = @"Contact Us"
}
} else {
if(indexPath.row == 0) {
cell.textLabel.text = @"nickoneill"
} else if (indexPath.row == 1) {
cell.textLabel.text = @"objctoswift"
} else {
cell.textLabel.text = @"@whyareyousodumb"
}
}
你需要不断的拆分这些段落和索引行,而且这段代码中有着大量的嵌套,看上去让人晕晕乎乎的,如果你在选择了这样的写法,那么在之后的编码过程中,你就要不断地复制这段代码。于是,代码的体积就会异常庞大,内容也会显得非常杂乱,编程人员肯定不会喜欢这样的事情。
Swift下的表达方式
let shortPath = (indexPath.section,indexPath.row)
switch shortPath {
case (0,0):
cell.textLabel.text = "Twitter"
case (0,1):
cell.textLabel.text = "Blog"
case (0,2):
cell.textLabel.text = "Contact Us"
case (1,0):
cell.textLabel.text = "@nickoneill"
case (1,1):
cell.textLabel.text = "@objctoswift"
case (1,2):
cell.textLabel.text = "@whyareyousodumb"
default:
cell.textLabel.text = " ?\_(θ)_/ ?"
}
而在Swift下,解决同样的问题,代码就会变成这样。代码变短了,也更清晰了,哪个编程人员不喜欢这样的代码?
所有的section都整齐的排列,你可以轻松的分辨section和row。如果你看到了枚举之外的语句,你也许应该考虑一下它对枚举会起到什么样的作用。
Swift显然是最好的方式
enum TwitterHandles: Int {
case Nickoneill
case Objctoswift
case Whyareyousodumb
func labelText() -> String {
switch self {
...
}
}
}
let rowData = TwitterHandles(rawValue: indexPath.row)
cell.textLabel.text = rowData.labelText()
Swift的编写方式显然要比Objective-C更好。这段代码中有一个枚举,一个整数的原始数值,它代表了Tabel vIEw cell中的一个section,这样就保留了这里的指令,没意思当我们创建一个table cell的时候,我们其实都是在使用当前正在处理的row来创建这个枚举对象。
之后,我们会命令枚举对象生成适当的单元值。我们放弃的不仅仅是这些单元对于枚举的命令,还有这些单元在枚举中组织起来的内容。这样,我们就将所有东西放在了一起,从而让代码变得简洁。同时,如果我们想要在中间添加新的代码,也不用像在使用Objective-C语言的时候一样,对所有section和索引进行调整。
我们对最佳Swift模式的看法将会随时间改变
何为最佳Swift模式?
我对这个问题的看法曾经出现过改变,这种改变让我自己都非常惊讶,在使用Swift的过程中,我不断遇上优秀的模式,它们有点像是隐藏在这种语言中的秘密。刚开始你在使用某种方法解决一个问题,因为你已经对这种方式非常熟悉。但是突然有一天,在和另一个人的合作过程当中,他教了你另一种解决问题的方式,你一定会非常惊讶。在学会一种新模式之后,你一定迫不及待的想要使用它,你会觉得这个模式可以在各个地方都适用。但是事实并非如此,并不是我们所做的每一次改进,都能成为优秀的模式。将复杂的代码简化成一行代码,并不一定能让它成为更好的东西,它并不是放之四海而皆准的定律。
并不是每一条简化后的代码都是优秀的模式
dispatch_async(dispatch_get_global_queue(disPATCH_QUEUE_PRIORITY_DEFAulT,0),^{
// do some task
dispatch_async(dispatch_get_main_queue(),^{
// update some UI
});
});
我们来看看Objective-C语言中的另一个例子。加入你正在后台线程代码中进行某种处理,而这个处理需要花费大量的时间。在你完成之后,你又回到主线程来升级UI。
在Swift下,我们可以用几行简短的句法完成这项工作,然后将其运用在许多地方。
func mainToBackground(background:() -> (),main: () -> ()) {
let priority = disPATCH_QUEUE_PRIORITY_DEFAulT
dispatch_async(dispatch_get_global_queue(priority,0)) {
background()
dispatch_async(dispatch_get_main_queue()) {
main()
}
}
}
mainToBackground({
// do some task
},{
// update the ui
})
我们一共要进行两次闭包,一个在背景线程中运行。之后我们在进行第二次闭包,让它在主线程中运行。那么问题来了,你觉得我们能够在Swift下继续深化,对不对?我们可能太乐观了。
{ /* do some task */ } ~> { /* update some UI */ }
上面是一个自定义的 *** 作符,左边的闭包运行在背景线程中,右边的闭包运行在主线程中,对不对?当然,但是它并不清楚其意图,如果你在左边或是右边的闭包里放入大量代码的话,这个 *** 作符就会迷失方向。当你使用代码库的时候,如果你一定要学习这些不标准的模式,这并不是聪明的工作方式。我觉得你也不会喜欢这样的项目。这是好的模式吗?并不是,它只是看上去比较简洁而已。
所谓优秀的模式,要在维持简洁性的同时提供方便性
优秀的模式,要在维持代码简洁度的同时,为你提供方便。我们所瞄准的,应该是那些第一次写代码的人,那些第一次接触到Swift的人,要让这些人也能轻松理解这些模式。无论这些人最先接触的是哪种语言,他们已经被各种 *** 作符搞得焦头烂额了。因此,我对于自定义 *** 作的态度是:不要碰这种东西。
建立能给视图控制器添加视图的模式
class VIEwController: UIVIEwController {
let imageVIEw = UIImageVIEw()
let gobutton = UIbutton()
overrIDe func vIEwDIDLoad() {
imageVIEw.image = UIImage(named: "profile")
vIEw.addSubvIEw(imageVIEw)
gobutton.frame = CGRect(x: 0,y: 0,wIDth: 30,height: 30)
gobutton.setTitle("GO!",forState: .normal)
vIEw.addSubvIEw(gobutton)
}
}
上面是一个传统的设置模式,用来给视图控制器添加视图。但是它并不算是一个模式,甚至还比不上直接把所有东西都丢进vIEwDIDLoad里。我和所有人一样,对于出现这样的模式负有一定责任。尽管这里只有两个子视图,但是已经出现了难以控制的趋势。如果视图的数量增多,或是视图控制器变得复杂一些,这种编程方式会很快失去控制。在这样的情况下,我们能做点什么?
在Swift中,初始化闭包是我最喜欢的模式之一。
class VIEwController: UIVIEwController {
let imageVIEw: UIImageVIEw = {
let imageVIEw = UIImageVIEw()
imageVIEw.image = UIImage(named: "profile")
return imageVIEw
}()
let gobutton: UIbutton = {
let button = UIbutton()
button.frame = CGRect(x: 0,height: 30)
button.setTitle("GO!",forState: .normal)
return button
}()
overrIDe func vIEwDIDLoad() {
vIEw.addSubvIEw(imageVIEw)
vIEw.addSubvIEw(gobutton)
}
}
在上面这段代码中,我们是要创建一个可以配置我们所需种类的闭包,并且将其返回。这就是这段代码的作用。当类被装载完成之后,它会被立即调用。它还可以轻松的将这个单一、巨大的vIEwDIDLoad拆分成独立的初始化闭包,这样一来,你就再也不用猜测每个区域内究竟发生了什么事了。如果在初始化闭包中,它就是在配置视图,返回视图。
我这样喜欢模式的原因,在于我终于不用把所有东西都丢进vIEwDIDLoad里面了。我终于可以使用独立的视图生命周期,视图最初的意图本来就是这样的。
这样的代码编写方式够清晰吗?当然,它足够简洁吗?虽然它看上去有一些样板花,但是相比于杂乱的vIEwDIDLoad,它还是作古简洁的。这些模式非常适合你用来配置视图,我也高度推荐。
我们还可以使用类似的模式来处理故事板视图配置。
@IBOutlet weak var arrivalLabel: UILabel! {
dIDSet {
arrivalLabel.text = "Arriving in 10 minutes".uppercaseString
arrivalLabel.Font = UIFont(name: "Lato",size: 11)
arrivalLabel.textcolor = UIcolor.bluecolor()
arrivalLabel.textAlignment = .Center
arrivalLabel.numberOflines = 1
}
}
故事板非常适合自动排版,但是它在依赖注入方面却不够好,而且非常不擅长处理视图配置。因此我更倾向于在故事板中加入大量的基本排版,然后在视图控制器中对视图进行配置。
在使用IBOutlet完成了UILabel的配置之后,它会运行在所有这些配置之上。
可选值是一个非常好的工具,它可以用来对应用中的数据建模,但是同时它也会在相对简单的 *** 作中添加大量代码。例如下面这个:
overrIDe func vIEwDIDAppear(animated: Bool) {
super.vIEwDIDAppear(animated)
if let paths = tableVIEw.indexPathsForSelectedRows {
for path in paths {
tableVIEw.deselectRowAtIndexPath(path,animated: true)
}
}
}
加入你从另一个试图控制器中回到vIEwDIDAppear里,你想要取消对所有索引路径的选择。虽然很好理解,但是在实际 *** 作的时候,你会发现这里有很多额外的代码,这些代码与当前的 *** 作无关。这些代码就是可选数组所带来的。我们可以使用很多工具来帮助我们处理这些代码。例如forEach就可以处理可选数组,我们可以用它来替代整个可选值。
tableVIEw.indexPathsForSelectedRows?.forEach({ (path) in tableVIEw.deselectRowAtIndexPath(path,animated: true )})
如果这里没有东西,它就不会进行任何 *** 作,如果这里有索引路径,它就会给我们提供每一条路径,之后我们就可以对其进行取消选择 *** 作了,然后移除一些不必要的注释。我们甚至还可以使用结尾闭包把它变得更简洁,用实参位置来替代实参名称。
tableVIEw.indexPathsForSelectedRows?.forEach{
tableVIEw.deselectRowAtIndexPath(},animated: true )
简单的模式可以替代大量的依赖
在这个例子中,我觉得这是一个可以接受的交换。很明显你得到了索引路径。这些路径只在一个情况下会被使用,那就是这个闭包更复杂的情况下。
let Json = try? NSJsONSerialization.JsONObjectWithData(data,options: [])
很多看上去很复杂的东西,其实它们原本并不需要这么复杂。很多时候,你并不需要一些体积很大的框架,因为一个简单的模式就可以起到相同的效果。下面是一个我非常喜欢的例子,它是一个有关JsON的例子:拆箱。
if let object = Json as? Dictionary<String,AnyObject>,
places: [Place] = UnBox(object) {
return places
}
struct Place: UnBoxable {
它有着很多优秀的功能,可以将复杂的JsON变成结构体。在处理网络问题上,我经常使用它。但是,在很多时候,我并不需要这样做。如果你正在使用网络管理员程序,做过JsON序列化的人都清楚,你需要提取数据,将其转化为某种对象,我们要验证它是某种代码字典,然后将其拆箱,之后及期望于它能给我提供结构清晰的结构体。
let ID: String
let address: Int?
let text: String
init(unBoxer: UnBoxer) {
self.ID = unBoxer.unBox("ID")
self.address = unBoxer.unBox("address")
self.placename = unBoxer.unBox("place_name")
}
}
struct Place {
我们必须使用专门的初始化程序来建立结构体。然而你只是得到了一些基本的数据,上面缺少了很多额外的代码,你只能自己去写这些缺失的代码。而如果你可以使用可失败构造器模式的话,你就可以获得更好的效果。在对特定数据进行拆箱的时候,其他的代码也又可能会默认出现。
// ...
init?(Json: Dictionary<String,AnyObject>) {
guard let ID = Json["_ID"] as? String else {
return nil
}
self.ID = ID
self.address = Json["address"] as? Int
self.placename = (Json["name"] as? String) ?? "No place name"
}
}
架构是一种需要时间积累才能学会的东西
而如果这个过程失败了,你也可以清楚的知道去哪里排除错误,找到究竟是哪里出现了问题。而不需要像在框架里排除错误那样去在某个选择器上设定断点。
寻找新模式的技巧,将大规模的架构组合在一起,需要更多的时间。在编程生涯中,你将会获得许多机会,来开发自己的架构决策技巧,这些技巧之后会变成模式,让你运用在未来的工作中,例如网络、核心数据等,还有与应用交互的方式,例如通知中心和KVO委托等。
模式是好东西,我们都热爱模式,我们总是可以不断丰富它,你也可以打造属于自己的模式,现在你要做的就是学会如何开始使用和创建模式。
1. 培养自己对代码的直觉。
2. 在Swift中打开一个playground,或是新建一个项目,在上面尝试一些新的模式。任何你所恐惧的代码都可以变得简单。任何看上去类似Objective?C的代码,都是一个机会,你很有可能把它变成看上去更直观的东西,正因为次,Swift才会成为更适合你的编程语言。如果项目不着急,有时间的时候你完全可以自己去试验一下,这种试验对你是大有裨益的。
3. 重新阅读语言向导说明书。聚沙成塔,你将会最终利用这些新的技能开始一项真正的项目。即使你先发现的模式并不符合当前项目的需要,这对你来说也是一次实验,一个学习的过程。未来不一定什么时候你就可以用上这些模式。
4. 时刻牢记保持代码的可读性。这份说明是事实上有着密度极大的内容,在你刚开始学习Swift这种语言的时候,你可能会觉得这份说明书将的东西都非常基础,但是里面其实隐藏着很多宝藏,你在最初的时候很可能没有意识到这些东西的存在。当你带着疑问再去看这份说明书的时候,你就会发现它的价值。在实践了一段时间Swift之后,有的时候只是不经意的一撇,这份说明书就能够让你对Swift的理解更加深刻一些。我个人就经历了这个过程,在遇到问题的时候我就会重新读一篇这份指导,然后在里面找到解决问题的办法。我总是在想:“这份向导说明太实用了。我以前怎么没实用它?”
你很可能会陷入简化代码的漩涡中无法自拔。但是你应该想想那些第一次使用Swift的人,你的代码能否让这些人轻易理解?他们能否看懂你的代码是在尝试解决哪个问题?
总结以上是内存溢出为你收集整理的探索原生Swift的模式全部内容,希望文章能够帮你解决探索原生Swift的模式所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)