您所在的位置:主页 > iOS开发培训 >

Swift最酷炫的七大功能

时间:2014-07-08 10:05来源:未知 作者:疯狂java 点击:


  摘要:Swift语言中有哪些让iOS和OS X开发者颇为兴奋的部分?与Ruby、C++、Objective-C语言等语言相比,Swift又有哪些优势?本文作者在花费大量时间对Swift进行研究之后,总结出了Swift最为酷炫的七大功能。

  本文作者Andrew Wagner是一名资深的iOS和Web开发者,在Swift面世之后,他花费了许多的时间来研究Swift,在文章中,他不仅分享了Swift语言中让iOS和OS X开发者颇为兴奋的部分,还将其与Ruby、C++、Objective-C语言进行比较,总结出了Swift最为酷炫的七大功能。

  1. 支持扩展结构和字面量

  Swift中我最喜欢的一点就是可以扩展结构,这对于向现有结构中添加函数可谓大有裨益。对我来说,最好的例子就是它能够实现向支持返还矩形类Rectangle中心点的CGRect添加一个中心法:

  [cpp]

  extension CGRect {

  var center : CGPoint {

  return CGPoint(

  x: self.origin.x + self.size.width / 2.0,

  y: self.origin.y + self.size.height / 2.0

  )

  }

  }

  我不得不使用大量的Rectangle中心点,而如果我可以利用name来取代繁杂的数学计算,就能够让代码的意图显得更加清晰明了。

  此外,Swift还支持扩展字面量。在Ruby中,有一个名为repeat的非常有用的整数方法。基本上你可以使用块来对一个整数调用repeat,即使重复定义整数也可被执行。然而在Swift中,一切就简单得多:

  [cpp]

  extension Int {

  func repeat(block : () -> ()) {

  for i in 0..self {

  block()

  }

  }

  }

  接着,你就可以像这样使用它:

  [cpp]

  3.repeat {

  // called 3 times

  println("hello")

  }

  注:如果唯一参数为闭包,则可省略括号。

  2. 更加灵活的枚举

  Objective-C的枚举语法着实欠佳,而Swift中的枚举则更加灵活,你可以定义Swift的枚举存储任何类型的相关值,例如,网络请求:

  [cpp]

  struct NetRequest {

  enum Method {

  case GET

  case POST(String)

  }

  var URL : String

  var method : Method

  }

  var getRequest = NetRequest(URL: http://www.fkit.org, method: .GET)

  var postRequest = NetRequest(URL: "http://www.fkit.org", method: .POST("{\"username\":

  \"drewag\"}"))

  GET请求并不具备请求主体,但POST却具备。在方法枚举中,如果没有一个潜在闲置的成员变量,则可以直接定义POST内容主体。

  关于这点,最好的例证就是Swift的Optional。Swift编译器能够给予我们许多语法上的甜头,但落到实处,当你像这样定义一个可选字符串时:var myString : String?,编译器会将其转换为var myString : Optional。定义Optional:

  [cpp]

  enum Optional {

  case None

  case Some(T)

  }

  3. 更为强大的泛型

  集合在Objective-C中使用的都是最通用的泛型类,这就意味着开发者可以将任何值带入一个包含混合对象的集合当中,但却会造成集合类型模糊。这也是我所认为的C++比Objective-C更好的一个地方,C++有允许定义具备诸如vector等特定类型的集合的Templates。Swift则借用了极为相似的语法,和可根据自我需求定义写出灵活可重用的函数及类型的泛型(Generics)代码。

  接下来,让我们来看一下不仅省时更能避免Bug出现的泛型的一个简单示例:

  [cpp]

  class Word {

  enum PartOfSpeech {

  case Noun, Pronoun, Verb

  }

  var value : String

  var partOfSpeech : PartOfSpeech

  init(_ value: String, _ partOfSpeech : PartOfSpeech) {

  self.value = value

  self.partOfSpeech = partOfSpeech

  }

  }

  var sentence = [Word("I", .Pronoun), Word("ran", .Verb), Word("home", .Noun)]

  class Word {

  enum PartOfSpeech {

  case Noun, Pronoun, Verb

  }

  var value : String

  var partOfSpeech : PartOfSpeech

  init(_ value: String, _ partOfSpeech : PartOfSpeech) {

  self.value = value

  self.partOfSpeech = partOfSpeech

  }

  }

  var sentence = [Word("I", .Pronoun), Word("ran", .Verb), Word("home", .Noun)]

  sentence.append("quickly") // Cannot convert the expression's type '()' to type 'Word'

  sentence[0].lowercaseString // Could not find member 'lowercaseString'

  sentence[0].value.lowercaseString

  4. 不同类型多重函数

  在Swift中,当定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入,也可以定义某种类型的值作为函数执行结束的输出。这对于定义可适用于各种类型但需要不同实现的函数来说绝对是如虎添翼。

  [cpp]

  func mathmaticallyAdd(a : Int, b : Int) -> Int {

  return a + b;

  }

  func mathmaticallyAdd(a : String, b : String) -> String {

  let aNum = a.bridgeToObjectiveC().intValue

  let bNum = b.bridgeToObjectiveC().intValue

  return "\(aNum + bNum)"

  }

  mathmaticallyAdd(2, 7) // returns 9

  mathmaticallyAdd("2", "7") // returns “9”

  在上面的代码段中,无缘无故地定义了mathmaticallyAddInts和mathmaticallyAddStrings两个不同的函数,因为其用意很清楚,只需mathmaticallyAdd即可,那这样就会显得过于繁琐冗长。定义使用Int,函数可以只使用+操作符,然而,在字符串实现中,该函数必须首先将字符串转换为整数。

  5. 属性监视器

  在Objective-C中,很多情况下我都会重写属性setter,以便我能在执行某个操作前后对值进行设置。如果我不想将属性设置为atomic的话,那么我就需要引用代码来充当实际值分配到我想要执行的那个操作上,这样实在是太笨拙不堪了。

  反观Swift,它拥有一种内置的机制,能够在移除对样板代码的需求任务完成前后进行监控并响应,这项功能称之为属性监视器(Property Observers)。你可以为属性添加一个willSet或(和)一个didSet监视器,willSet在设置新的值之前调用,而didSet则是在新的值被设置之后立即调用。

  [cpp]

  class MyView : UIView {

  var aSubview : UIView {

  didSet {

  oldValue.removeFromSuperview()

  self.addSubview(aSubview)

  }

  }

  init(frame: CGRect) {

  self.aSubview = UIView()

  super.init(frame: frame)

  }

  }

  willSet监视器会将新的属性值作为固定参数传入,在willSet的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,而其默认名称用newValue表示。同样,didSet监视器也会将旧的属性值作为参数传入,可以为该参数命名或直接使用默认参数名oldValue。

  6. 优雅的闭包占用列表

  尽管Swift的内存管理仍然采用自动引用计数,但其对于语法却有着极大的改善。在Swift中,无需在块以外声明weak或unsafe_unretained变量,你可以定义一个闭包应该如何使用捕获列表来捕获常量或变量。

  [cpp]

  class FilePickerController : UIViewController {

  var onDidPickFileWithPath : ((path : String) -> ())?

  }

  class DocumentViewController : UIViewController {

  var filePicker : FilePickerController?

  var content : String?

  init(fileAtURL URL: NSURL) {

  super.init(nibName: nil, bundle: nil)

  var request = NSURLRequest(URL: URL)

  // weak: Have self automatically set to nil if it is deallocated

  NSURLConnection.sendAsynchronousRequest(request, queue: nil) { [weak self]

  (response, data, error) in

  if let actualSelf = self {

  // Captures an owning reference "actualSelf" so interacting with it in this

  // block is safe

  actualSelf.content = NSString(data: data, encoding: NSUTF8StringEncoding)

  }

  }

  }

  func presentFilePicker() {

  if !filePicker {

  filePicker = FilePickerController();

  // unowned: Don't worry about the nil case if you know it will never happen

  filePicker!.onDidPickFileWithPath = { [unowned self]

  (path : String) in

  self.content = NSString.stringWithContentsOfFile(path) as? String

  }

  }

  self.presentViewController(filePicker!, animated: true, completion: nil)

  }

  }

  Swift提供了一种颇为优雅的方法来解决循环强引用所存在的问题,称之为闭包占用列表(Closuer Capture List)。在上段代码中,除了可以初始化URL的DocumentViewController之外,我还定义了一个FilePickerController类。当用户选择一个文件时,它便会提供一个闭包成员,而DocumentViewController则有一个方法来呈现该文件并处理用户选择文件的操作。此时,如果该闭包强烈捕获self,则将产生循环强引用。

  这种情况下,我明白只要有DocumentViewController,闭包也就会永远存在,所以我可以通过[unowned self]使用无主引用来解决循环强引用,这比Objective-C可要干净得多了。

  7. 构造过程更安全

  在使用Objective-C编写程序时,我常常会犯一个错误,总是要我抓狂地花上好几分钟去调试它。这个问题非常普遍,就是在我想加载内容进去的一个类中,有一个可变数组,为了能够被添加进去,首先需要初始化数组,但我却老是会忘记。

  而在Swift中,编辑器将会处于两方面的原因截获它,其一便是你必须对取值为空的属性进行处理,在定义一个构造器之后,如果你留下任何未初始化的成员变量,编译器便会显示错误,这将节省我大量的时间,如下:

  [cpp]

  class FileSystemItem {

  var path : String

  init(path : String) {

  self.path = path

  }

  }

  class Directory : FileSystemItem {

  var contents : FileSystemItem[]

  init(path : String) {

  self.contents = [] // required

  super.init(path: path) // required

  }

  func loadContents() {

  // ...

  // self.contents.append(file)

  // ...

  }

  }

  在Objective-C中有一个名为指定构造器(Designated Initializers)的概念,每个类都应该有一个将全部类都初始化在安全状态的构造器,而其他构造器则应该调用一个指定构造器来确保类良好,但很遗憾这在Objective-C中并不是强制执行的。而Swift则将其融入编译器,这就意味着Directory类必须调用init(path)构造器,否则将显示错误,而在path没有被定义的情况下,该类将无法实现,当类想要获得更大的继承时,便显得尤为重要。现在,您还可以进入Swift的mobilehub主页进行资源分享和讨论。