程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> 初步懂得Swift中的泛型

初步懂得Swift中的泛型

編輯:更多關於編程

初步懂得Swift中的泛型。本站提示廣大學習愛好者:(初步懂得Swift中的泛型)文章只能為提供參考,不一定能成為您想要的結果。以下是初步懂得Swift中的泛型正文


假如你曾經著手寫過Swift的法式,信任你曾經懂得了Swift說話的常識,好比若何寫類(class)和構造體(struct)。但Swift可沒這麼簡略,呵呵呵。這篇教程重要講述Swift的一個強力的特征:泛型。這個特征在許多法式設計說話裡都異常受迎接。

關於類型平安(type-safe)說話,一個罕見的成績就是若何編寫實用於多品種型輸出的法式。想象一下,兩個整型數相加和兩個浮點數相加的法式看起來應當異常相似,乃至如出一轍才對。獨一的差別就是變量的類型分歧。

在強類型說話中,你須要去界說諸如addInts, addFloats, addDoubles 等辦法來准確地處置參數及前往值。

很多編程說話曾經處理了這個成績。例如,在C++中,應用Template來處理。而Swift,Java和C#則采取了泛型來處理這個成績。泛型,也是這篇文章要重點引見的。

在這篇文章中,你將會學到Swift中若何應用泛型,或許你曾經接觸過,或許沒有,不外沒緊要,我們會來逐個摸索。然後,我們會創立一個Flicker圖片搜刮運用,這個運用應用了自界說的泛型數據構造來保留用戶搜刮的內容。

備注:本文假定你曾經對Swift有根本的懂得或許有過Swift開辟經歷。假如你第一次接觸Swift或許對Swift不是太懂得,建議你起首浏覽下other Swift tutorials。

泛型引見

或許你不曉得這個術語,但信任你曾經在Swift中見到它了。Swift中的數組和字典類型就是應用泛型的經典例子。

Object-C開辟者曾經習氣應用數組和字典去保留多種數據類型。這類方法供給了很年夜的靈巧性,然則誰又能曉得一個API前往的數組外面究竟是啥(數據類型)呢?你獨一能做的就是檢查文檔或許檢查(辦法的)變量敕令(這也是別的一種文檔喲!)。即便你檢查了文檔,你也不克不及包管法式在運轉期不發生bug或許其他異常。

比擬Object-C,Swift中的數組和字典都是類型平安的。一個Int型數組只可以保留Int而弗成以保留String。這意味著你不消再檢查文檔啦,編譯器便可以幫你做類型檢討,然後你就就快可以高興地coding了!


例如,在Object-C的UIKit中, 在自界說的View外面處置觸摸事宜可以這麼寫:
 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

上述辦法外面的set只可以保留UITouch實例, 由於文檔外面就是這麼說的。因為這個聚集外面可以聽任何對象,所以你須要在代碼外面停止類型轉換,也就是說把touches外面的對象轉為UITouch對象。

以後Swift的尺度庫外面沒有界說聚集對象,然則你可使用數組來取代聚集對象,你可以用swift重寫下面的代碼:
 
func touchesBegan(touches: [UITouch]!, withEvent event: UIEvent!)

下面的代碼明白告知你 touches數組只可以包括 UITouch實例, 不然編譯器就會報異常。如許一來,你就再不消去做那煩人的類型轉換了,由於編譯器給你做了類型平安檢討,包管數組外面只許可有 UITouch對象。


扼要說來,泛型為類供給了一個類型參數。一切的數組都有雷同的感化,即按次序貯存變量的數值,泛型數組除多了一個類型參數以外,沒有其他的分歧的地方。也許如許想更輕易懂得:你將要運用在數組上的各類算法和貯存的數值類型有關,是以這些算法關於泛型數組和非泛型數組都實用。

既然你曾經明確了泛型的基本常識和用法,那我們就開端把它運用在一個詳細的例子上吧。
泛型實例

為了測試泛型,你將會編寫一個在Flickr上搜刮圖片的運用。

起首請下載這個法式雛形,並盡快熟習外面重要的類。個中Flickr類用於和Flickr的API交互。請留意這個類外面包括了一個API key(平日用於用戶受權—譯者注),但假如你想要擴大這個運用的話能夠須要用本身的key,注冊請點我。


結構並運轉這個運用,你會看到這個:

似乎甚麼都沒有?別急,用不了多久你便可以讓它幫你抓取心愛的喵圖了!
有序字典(原文Ordered Dictionaries)

你的運用會依據每一個用戶的查詢情形下載圖片,依照圖片被搜刮到的頻率由高到低排序並顯示。

但假如用戶對異樣的症結字搜刮了兩次會如何?假如這個運用能顯示前次搜刮的成果就行了。

也許用數組來完成這個功效也行得通,但為了進修泛型,你將會應用一個全新的數據構造:有序字典。


和數組分歧的是,包含Swift在邊疆許多編程說話和框架都不包管聚集(sets)和字典(dictionaries)的數據存儲次序。有序字典和通俗的字典相似,分歧的地方在於它的key是有序的。你將會用這個特征,依據搜刮症結字按次序存儲搜刮成果。如許存儲的利益是可以疾速查詢並更新圖片列表。

一個輕率的設法主意是自界說一個數據構造處置有序字典。然則你須要加倍有前瞻性才行!你必需斟酌到若何讓你的運用在將來幾年內都能正常任務!是以在這裡應用泛型再適合不外了。
初始數據構造

點擊“文件\新建\文件...”新建一個文件,並選擇“IOS\Source\Swift File”。點擊“下一步”並把這個文件定名為“OrderedDictionary”。最初,點擊“創立”。

你會獲得一個空的Swift文件,加如許一段代碼出來:
 
struct OrderedDictionary {  }

到如今為止應當都沒有甚麼成績。經由過程語義可以看出這個對象是一個構造體。


留意:總之,值的語義可以想象為“復制、粘貼的行動”,而不是“分享、參考的行動”。值的語義帶來一系列的利益,例如不消擔憂一段代碼有意地修正你的數據。懂得更多,點擊"Swift by Tutorials"的第三章節:類和構造體。

如今你須要將其普通化,以便它可以或許裝載你須要的任何類型的數據。經由過程以下轉變你對Swift中“構造”的界說:
 
struct OrderedDictionary<KeyType, ValueType>

在尖括弧中的元素是通用類型的參數。KeyType和ValueType不是他們本身的類型,而是你可使用在構造裡界說代替的類型。如今就簡練清爽很多了!

最簡略的完成一個有次序的字典是堅持一個數組和一個字典。字典中將會裝載衍射,而數組將裝載keys的次序。


在構造體外部的界說中,參加以下的代碼:
 
typealias ArrayType = [KeyType]typealias DictionaryType = [KeyType: ValueType] var array = ArrayType()var dictionary = DictionaryType()

如許聲明有兩個目標,就像上例描寫的,有兩品種型的用於給曾經存在的類型的取新的稱號的別號。在這,你將分離地為前面的數組和字典賦值了別號。聲明別號是將龐雜類型界說為更短稱號的類型的一種異常有用的方法。

你將留意怎樣樣從構造體中界說用“KeyType”和“ValueType”的參數類型中調換類型。上例的"KeyTypes"是數組類型的。固然這是沒有如許的類型的“KeyType”;當在普通的實例化時,將替換Swift像對OrderedDictionary的類型的一切類型經由過程。

就由於如許,你將會留意到編譯毛病:
 
Type 'Keytype' does not conform to protocol 'Hashable'

也許你會驚訝怎樣會如許?請再不雅察下Dictionary的繼續者:
 
struct Dictionary<KeyType: Hashable, ValueType>


除在KeyType以後的HashTable, 其他的都和OrderedDictionary的界說特殊的類似。在分號前面為KeyType聲明的Hashable,必定相符Hashable的協定。這是由於字典須要為hash key完成。

用這類方法束縛泛型參數長短經常見的。例如,你想要根據你的運用應用參數做甚麼,來束縛值的類型以,確保相等性、可打印性協定。

翻開OrderedDictionary.Swift,用下例來代替你對構造體的界說:
 
struct OrderedDictionary<KeyType: Hashable, ValueType>

如許為OrderedDictionary聲明KeyType,必需相符Hashable。這就意味著,不管KeyType釀成甚麼類型,都可以接收為沒有聲明的字典的KEY。

如許,文件再次編譯,將不會報錯!

Keys, Values 和一切的這些趣事

假如不克不及為字典添加值,那末字典有甚麼感化了?翻開OrderedDictionary.swift,在你的構造體界說中添加以下函數:
 
// 1mutating func insert(value: ValueType, forKey key: KeyType, atIndex index: Int) -> ValueType?{
  var adjustedIndex = index
   // 2
  let existingValue = self.dictionary[key]
  if existingValue != nil {
    // 3
    let existingIndex = find(self.array, key)!     // 4
    if existingIndex < index {
      adjustedIndex--
    }
    self.array.removeAtIndex(existingIndex)
  }   // 5
  self.array.insert(key, atIndex:adjustedIndex)
  self.dictionary[key] = value
   // 6
  return existingValue}

上面引見一些新的特征。讓我們一步一步來引見:

  •     拔出一個新對象的辦法,insert(_:forKey:atIndex),須要三個參數:一個特殊的key的值,拔出一對key-value的索引。這是你之前沒有留意到的一個症結字:轉變。
  •     構造體的設計是默許不變的,這意味著平日你在實例化的辦法中,不克不及轉變構造的成員變量。這非常無限,你能添加轉變的症結字,並告知編譯器這個方法在構造體中是許可轉變的。這將贊助編譯器做出決議甚麼時刻復制構造體(他們是寫時復制的),也有助於API的編檔。
  •     你為字典的索引器輸出一個假如曾經存在,那末前往已存在的值的key,這個拔出辦法模仿字典更新值雷同的行動,是以為這個值堅持曾經存在的值。
  •     假如這有一個曾經存在的值,只要如許函數能力為這個值在數組裡找出索引。
  •     假如這個曾經存在的key在拔出索引的之前,這時候你須要調劑拔出的索引,由於你須要移除曾經存在的key。
  •     你將恰當地更新數組和字典。
  •     最初,你前往已存在的值,當這也許沒有已存在的值,這個函數前往一個可選的值!


如今你可認為字典添加移除值?

像以下對OrderedDictionary構造體的界說的函數:
 
// 1mutating func removeAtIndex(index: Int) -> (KeyType, ValueType){
  // 2
  precondition(index < self.array.count, "Index out-of-bounds")   // 3
  let key = self.array.removeAtIndex(index)   // 4
  let value = self.dictionary.removeValueForKey(key)!   // 5
  return (key, value)}

如今再讓我們一步一步剖析:

1.這是轉變構造體狀況的函數,removeAtIndex的稱號須要和數組的辦法婚配。適當的時刻,斟酌應用鏡像體系庫中API是不錯的選擇。如許贊助開辟者在他們的任務平台裡,異常輕易地應用你的API。

2.起首,你須要檢討索引,不雅察他們能否是在年夜量的數組裡。測驗考試著從未聲明的數組中移除越位的元素,將會招致超時毛病,一切在這時候檢討將會更早相符如許的情形。你也許在Objective-C中應用斷言函數;在Swift中止言也是可以使用的。然則條件是在釋放的工程中是運動的,不然你運轉的運用的將會終止。

3.接著,當同時從數組中移除值時,你在給定的索引中數組中取得值。

4.然後,你從字典中為這個key移除的值,同時也會前往這個值。也許在給出的key中,字典也沒有響應的值,所以removeValueForKey前往一個可選的。這類情形下,你曉得字典將會為給出的key,包括一個值,由於這是獨一的本身給字典添加值的辦法--insert(_:forKey:atIndex:),這時候你可以選擇應用“!”,注解這將會有公理感值。

5.最初,你在一個元組前往的key和value。數組的removeAtIndex和字典的removeValueForKey是一樣的前往已存在的值功效。

值的讀取跟寫入

把值寫入字典(dictionary)是沒成績了, 可是如許還不敷! 你還須要完成一個辦法(method) 從字典中讀出響應的值.

翻開 OrderedDictionary.swift 文件, 然後把以下代碼添加到構造界說(struct definition)傍邊, , 就放在 thearrayanddictionaryvariable 聲明的上面:
 
var count: Int {
  return self.array.count}

這個經常使用的屬性, 用來算出字典外面有幾筆記錄. 只需前往數組的 count 屬性的中值便可以了!

接上去, 就是若何拜訪(Access)字典中的記載了(Element). 我們可以經由過程下標(Subscript)來拜訪, 代碼以下:
 
let dictionary = [1: "one", 2: "two"]let one = dictionary[1] // Subscript

下標的語法我們會用了, 然則假如是我們本身界說的類那該怎樣用呢? 好在 Swift 支撐在自界說類外頭添加這項功效. 並且完成起來也不龐雜.


把以下代碼添加到構造界說的底部:
 
// 1
subscript(key: KeyType) -> ValueType? {
  // 2(a)
  get {
    // 3
    return self.dictionary[key]
  }
  // 2(b)
  set {
    // 4
    if let index = find(self.array, key) {
    } else {
      self.array.append(key)
    }     // 5
    self.dictionary[key] = newValue 
  }
}

我們具體說明下這段代碼:

  •     下面代碼正文中標有 1 的那一段:跟 func 和 var 相似, subscript 也是個症結字, 經由過程它界說下標.  參數 key 是湧現在中括號中的誰人對象.
  •     正文中標有 2 的那一段: 下標由 setter 跟 getter 兩部門構成. 本例同時界說了 setter (代碼中的 set  ) 跟 getter (代碼中的 get ). 固然, 不是每一個下標都要同時界說 setter 跟 getter.
  •     正文中標有 3 的那一段: getter 比擬簡略, 只需經由過程參數 key, 在字典中找到響應的值便可. 字典前往的是可選值(optinal), 假如 key 不存在, 該值為 nil.
  •     正文中標有 4 的那一段: setter 就龐雜些. 起首要檢測這個 key 在有序字典外面是否是曾經存在. 假如不存在, 則把 key 添加到數組中. 因為我們須要把 key 添加到數組的尾部, 所以這裡挪用的是 append 辦法.
  •     正文中標有 5 的那一段: 把值添加到字典中. 這裡用隱生命名的變量 newValue 獲得傳遞過去的值.

就像用 Swift 自帶的字典類的下標那樣去, 你可以經由過程 key 來查找某個值. 可是假如我們須要像拜訪數組那樣, 用下標索引(index)拜訪某個值, 該怎樣辦呢? 既然是有序字典, 沒有事理不克不及經由過程下標索引一個一個的按次序拜訪.


構造體跟類可以界說多個參數類型分歧的下標(subscript). 把以下代碼添加到構造界說的底部:
 
subscript(index: Int) -> (KeyType, ValueType) {
  // 1
  get {
    // 2
    precondition(index < self.array.count,
                 "Index out-of-bounds")     // 3
    let key = self.array[index]     // 4
    let value = self.dictionary[key]!     // 5
    return (key, value)
  }}

這段代碼跟後面那段相似, 分歧的是參數類型釀成了 Int. 由於我們如今要實的功效是現像數組那樣, 應用下標索引拜訪有序字典. 不外此次前往的是由 key 跟 value 構成的一個元組(tuple). 由於有序字典就是由如許一個一個的元組組成的.

上面詳細說明下這段代碼:

  •     這個下標只界說了 getter. 固然你也能夠把 setter 加上. 不外要留意先檢討 index 會不會越界.
  •     index 的值不克不及超越數組的界線, 也就是字典元組的個數. 我們可以應用 precondition 提醒開辟人員, 法式湧現越界拜訪.
  •     用 index 從數組中讀出 key.
  •     再用 key 從字典中讀取 value. 須要留意的是, 因為數組中的每個 key 跟字典的 value 是逐個對應的, 所以這裡應用符號 ! (unwrapped) 對讀出來的 value 拆包.
  •     最初, 前往一個包括 key 和 value 的元組.


挑釁: 為下面誰人下標完成 setter . 可以參考後面的例子.
提醒 1  


 留意, newValue 是個包括 key 跟 value 的元組.

 
提醒 2  


 以下代碼可以將值從元組中提掏出來:
  let(key, value) = newValue

 

或許你會獵奇, 假如 KeyType 是 Int 型的, 會湧現甚麼成績? 應用泛型的利益是, 不論是甚麼類型, 只需能算出哈希值(hashable)的就行,  所以 Int 固然也能用.  成績是, 當 key 也是 Int 型的時刻, 這倆個下標該怎樣辨別呢?

這就須要我們給編譯器供給更多的類型信息. 讓它曉得在甚麼時挪用哪一個下標. 好比我們界說的這兩個下標, 前往的類型紛歧樣. 假如你用 key-value 類型的元組給它賦值, 編譯器就會主動挪用誰人數組式(array-style )的下標.

在項目中測試

讓我們在現實項目中,試驗編譯揣摸應用的下標函數,和普通情形下,OrderedDictionary是怎樣任務的。

經由過程點擊"文件"、"創立"、"文件",新建一個項目,順次選擇"IOS"、"Source"、"Playground",再點擊下一步。然後點擊創立。

你必需得如許操作:復制和粘貼OrderedDictionary.swift全體到新建的項目中。由於不克不及在寫教程時你的運用模子中”看見”代碼

留意:這有一個處理辦法,可以代替復制、粘貼的方法。假如你須要將你運用的代碼參加到一個框架中,你的項目將接收的你代碼,就像Corrine Krych指出的如許。


如今,在你的項目底部參加以下的代碼:
 
var dict = OrderedDictionary<Int, String>()dict.insert("dog", forKey: 1, atIndex: 0)dict.insert("cat", forKey: 2, atIndex: 1)println(dict.array.description
        + " : "
        + dict.dictionary.description) var byIndex: (Int, String) = dict[0]println(byIndex) var byKey: String? = dict[2]println(byKey)

在側欄中(或許經由過程視圖/助理編纂/顯示助理編纂/),你將可以看到println()函數輸入的內容:

在這個例子中,字典有一個整數型的key,所以編譯器會審查被應用分派決議應用哪一個下標變量的類型。假如被應用的下標是一個(Int, String)的byIndex,編譯器會婚配希冀的前往值類型,應用數組類型的索引的下標。

假如你想從一個 byIndex 或許 byKey的變量中,移除類型的界說。編譯器將會報錯,注解編譯器不曉得應用哪個下標。


小貼士:因為編譯器是依照類型推理來任務的,所以須要明白地表現出類型。當存在多個有雷同的爭議的前往值類型的函數時,挪用者須要詳細化。須要留意:Swift中函數,可以“建-破”轉變。

經由過程在項目中,對有次序的字典的試驗中,你可以發明他的任務道理。在重返app之前,測驗考試從中添加、移除、和轉變key和value的類型。如今,你可以在你的次序字典中讀、寫操作!但要當心你的數據構造。如今你可以經由過程app來感觸感染個中的樂趣了!

添加圖片查找

如今是時刻讓你回過火來留意手中的app了。翻開 MasterViewController.swift。在兩個@IBOutlets 的上面,添加以下變量的界說:
 
var searches = OrderedDictionary<String, [Flickr.Photo]>()

你也許在迷惑,為何Flickr.Photo的類型中有個句號。那是由於Photo是在Flickr類的外部界說的類。在Swift中,如許的條理構造長短常有益的。它將有助於類的稱號冗長化。在Flickr的外部,你可以零丁應用Photo類,由於高低文關系告知了編譯器這是甚麼。這是次序化字典查詢用戶定閱的Flickr的功效。真像你看到的,包括查詢的字符串,和Flickr.Photo數組,或是從Flickr API 中前往的照片。留意,你在尖括號裡給出的key和value,將成為在詳細完成中KeyType和ValueType的參數類型。


接上去,找到表格視圖數據源的tableView(_:numberOfRowsInSection:)辦法,然後把它改成以下所示:
 
func tableView(tableView: UITableView,
               numberOfRowsInSection section: Int) -> Int{
  return self.searches.count}

這個辦法應用有序字典來告知表格視圖有若干行。接著,找到表格視圖數據源的tableView(_:cellForRowAtIndexPath:)辦法並把它改成以下所示:
 
func tableView(tableView: UITableView,
               cellForRowAtIndexPath indexPath: NSIndexPath)
              -> UITableViewCell{
  // 1
  let cell =
    tableView.dequeueReusableCellWithIdentifier("Cell",
      forIndexPath: indexPath) as UITableViewCell   // 2
  let (term, photos) = self.searches[indexPath.row]   // 3
  if let textLabel = cell.textLabel {
    textLabel.text = "\(term) (\(photos.count))"
  }
  return cell}

這是你在這個辦法中所做的:

1. 起首,從UITableView中挪出一個單位格。你須要把它直接轉換為UITableViewCell,由於dequeueReusableCellWithIdentifier仍然前往AnyObject(id in Objective-C),而不是UITableViewCell。也許在未來,蘋果公司會應用泛型重寫這部門API。

2. 接著,用你給的下標索引從指定的行獲得Key和Value,

3. 最初,恰當地設置單位格的文本標簽而且前往以後單位格。


如今讓我們試試鮮。找到UISearchBarDelegate 的拓展,就像以下的代碼一樣,轉變單例辦法。
 
func searchBarSearchButtonClicked(searchBar: UISearchBar!) {
  // 1
  searchBar.resignFirstResponder()   // 2
  let searchTerm = searchBar.text
  Flickr.search(searchTerm) {
    switch ($0) {
    case .Error:
      // 3
      break    case .Results(let results):
      // 4
      self.searches.insert(results,
                           forKey: searchTerm,
                           atIndex: 0)       // 5
      self.tableView.reloadData()
    }
  }}

當用戶點擊查詢按鈕時,這個辦法將會被挪用。以下就是在這個辦法中,你正做的事:

1.你第一反響是廢棄應用查詢框和鍵盤。

2.然後,你又會應用搜刮框查詢你輸出的文字,為查詢的文字而應用Flickr類來尋覓。Flickr的查詢辦法是:查詢術語,封閉履行查詢勝利或許掉敗。經由過程參數封閉:要不是毛病,那就是成果。

3.在毛病的情形下,不會產生任何事。然則你可以經由過程應用警報來提醒毛病,然則如今我們可以簡化如許的操作。代碼須要在這時候暫停會兒,告知Swfit編譯器你的毛病沒有任何反響的念頭。

4.假如查詢有用,將會在查詢成果中顯示相干的值。你將查詢的術語作為key參加到次序字典中。假如曾經在字典中存在了,將會把他放入到list的頂部,然後用最初的成果更新全部字典。

5.終究,因為你有新的數據,將再次加載table view。

哇!你的app將可以用於查詢圖片了!


構建並運轉app,做幾回查詢。你將會看到上面如許的一些器械:

如今再用與之前搜刮詞分歧的別的一個停止查詢. 你將會看到它跳到了頂部:

選擇一個查詢成果點擊去,你會發明它並沒有顯示照片。如今是時刻修復這個成績了!
給我看照片!

翻開MasterViewController.swift 並找到 prepareForSegue. 將它修正成上面如許:
 
override func prepareForSegue(segue: UIStoryboardSegue,
                              sender: AnyObject?){
  if segue.identifier == "showDetail" {
    if let indexPath = self.tableView.indexPathForSelectedRow()
    {
      let (_, photos) = self.searches[indexPath.row]
      (segue.destinationViewController         as DetailViewController).photos = photos    }
  }}

這是用了同創立單位項時拜訪被排好序的查詢成果字典一樣的辦法. 雖然沒有應用症結詞(檢索詞), 你也能夠用下劃線來顯示出元組的這個部門不須要被綁定到一個當地變量.


構建並運轉app,做一次查詢然後點出來。你會看到像上面如許的器械:

你好,小貓貓! 你有無想要收回快活的呼聲呢? :]

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved