Swift與OC詳談

最近瞄上了Swift,隨著3.0版本的發布,1.x,2.x的部分API發生了一些算是較大的變化,語法越來越簡便,實質內容還是保持不變,斷斷續續的接觸了Swift幾個月,總算是基本掌握了這門語言。Swift 采用安全的編程模式并添加了很多新特性,這將使編程更簡單,更靈活,也更有趣。Swift 是基于Cocoa 和 Cocoa Touch 框架演變而來。

Swift給我的感覺就像一個天資聰慧的靈童,彷佛什么都懂,既有現有語言優良的傳統(面向對象),又繼承了其兄OC另辟蹊徑的的方式(擴展,代理,面向協議),而蘋果為了更快的推廣這門語言,不惜放棄專制,居然開源了,雖然這很不蘋果,但誰都能看得出來這都是OC留下的禍根啊。Swift可以跨平臺運行,未來能不能在windows上玩一把就不得而知了。這里就不扯OC和Swift到底誰好誰壞,如:PHP是世界上最好的語言。

PS:在學習的過程中,收獲頗豐,借助老關的幫助,對比OC和Swift同步進階。不得不說老關的書作為初級,進階和工具書都是極好的,在此推薦一下:傳送門:Swift(關東升著)

一、基礎篇

.......................................

類型 Swift Object-C 備注
整型 Int8 Int32 UInt8 UInt64 NSInteger 32位平臺上 Int = Int32 (64 Int = Int64)
浮點型 Float Double NSFloat,NSDouble 32位浮點數,64位浮點數
布爾型 True, False YES,NO 嚴格true為真,false為假
字符型 Character NSString -
字符串 String NSString -
元組 Tuple () - (“1001”,“張三”,"18歲")
集合 Array,Dictionary,Set NSArray,NSDictionary,NSSet -
枚舉 Enum Enum -
結構體 Struct Struct -

1.可選類型

swift要求所有的數據類型聲明的變量或常量都是不能為nil的,所以必須對其進行初始化,初始化如果不確定其值的時候,可以采用可選類型(俗稱的(?)和(!)), 在不能保證可選類型值是否為空之前最好不要拆包,否則可能編譯報錯。他們兩個區別主要是在可選類型的拆包,拆包是將可選類型轉變成普通類型。拆包分為顯示拆包和隱性拆包。
例如 var n1 : Int? = 10 輸出內容為:Optional(10) 說明變量n1不是普通類型

  • 顯示拆包
    使用問號(?)聲明的可選類型在拆包的時候需要使用感嘆號(!)
var n1 : Int? = 10
print(n1! + 10)
  • 隱性拆包
    使用感嘆號(!)聲明的可選類型在拆包時不需要使用感嘆號(!)
var n2 : Int! = 10
print(n1 + 10)

2.Swift3.0字符串用法改動

  • swift3.0關于字符串string的部分API改動較大,這里列舉了一下:
import Foundation

//---------字符串定義-----------

//字符串變量
var str1 = "hello"

//字符串常量
let str2 = "swift3.0"

//聲明為nil,
var str3:String?  //可選類型

//空字符串
let str4 = String()
let str5 = ""     //提倡用這樣的字面量語法聲明,類型可不指定,swift自動識別

//字符
var char1:Character = "m"
var p_str1 = ""

//字符串拼接
p_str1 = str1 + str2
print(p_str1)
p_str1 = String(format:"%@~%@",str1,str2)
print(p_str1 as String);//hello~swift3.0
p_str1 = String(format:"%@~%@-%d",str1,str2,456)

print(p_str1);

//這種拼接方式方便地組合多種類型
p_str1 = "\(str1)\(str2)\(456)"
print(p_str1);//helloswift3.0456
p_str1 = "\(str1)\(str2)\(char1)"

print(p_str1);

//在字符串后面添加
p_str1.append(char1)
print(p_str1);
p_str1 += str2

print(p_str1);

//與數組通過指定字符拼接
var strArray = ["hello", "swift", "3.0"]
p_str1 = strArray.joined(separator: "-")//數組通過指定字符拼接

print("數組通過指定字符拼接\(p_str1)");


//拆分為指定的字符
strArray = p_str1.components(separatedBy: "-")
print("拆分為數組\(strArray)");

print("p_str1字符串為:\(p_str1)");


//枚舉字符
for ch in p_str1.characters{
    
    print(ch)
    
    switch ch {
    case "0":
        print("有該字符")
    default:
        break
    }
}


print(p_str1)

//字符串長度
print("p_str1長度為:\(p_str1.characters.count)");

//首字母
char1 = p_str1[p_str1.startIndex]
print("首字母\(char1)");

//末字母
char1 = p_str1[p_str1.index(before: p_str1.endIndex)]
print("末字母\(char1)")

//第二個字母
char1 = p_str1[p_str1.index(after: p_str1.startIndex)]
print("第二個字母\(char1)")

//索引4的字母
char1 = p_str1[p_str1.index(p_str1.startIndex, offsetBy: 4)]
print("索引4的字母\(char1)")


//字符串截取
let i = p_str1.index(p_str1.startIndex, offsetBy: 4)
let j = p_str1.index(p_str1.startIndex, offsetBy: 8)

print(i,j);

var subStr = p_str1.substring(to: i)
print("截取到4\(subStr)")

subStr = p_str1.substring(from: i)
print("從索引4開始截取\(subStr)")

subStr = p_str1.substring(with: i..<j)
print("從索引4開始到8(不截取索引為8的字符)結束截取\(subStr)")

//通過擴展來簡化一下

extension String{
    
    subscript (range: Range <Int>) -> String{
    
        get{
            
            let startIndex = self.index(self.startIndex, offsetBy:range.lowerBound)
            let endIndex = self.index(self.startIndex,offsetBy:range.upperBound)
            return self[Range(startIndex..<endIndex)]
            
        }
        
        set{
        
            let startIndex = self.index(self.startIndex,offsetBy:range.lowerBound)
            let endIndex = self.index(self.startIndex,offsetBy:range.upperBound)
            let strRange = Range(startIndex..<endIndex)
            
            self.replaceSubrange(strRange, with: newValue)
            
        }
        
    }
    
}

//通過擴展截取字符串
subStr = p_str1[0..<5]



print(p_str1)
//通過指定字符串截取子串
let range1 = p_str1.range(of: "o-")
let range2 = p_str1.range(of: ".")

subStr = p_str1.substring(from: (range1?.upperBound)!)
print("從o-開始截取字符串:\(subStr)")

subStr = p_str1.substring(with: (range1?.upperBound)!..<(range2?.lowerBound)!)
print("從o-開始到.結束截取字符串:\(subStr)")


print(subStr)
//插入指定字符串
subStr.insert("!", at: subStr.startIndex)
print(subStr);

subStr.insert("!", at: subStr.endIndex)
print(subStr);

subStr.insert(contentsOf:"YY".characters, at: subStr.index(after: subStr.startIndex))
print(subStr);

subStr.insert(contentsOf:"MM".characters, at: subStr.index(before: subStr.endIndex))
print(subStr)

//打印字符串的索引
let x = p_str1.index(p_str1.startIndex, offsetBy: 4)
print(x)

print(subStr)
//刪除指定字符串
subStr.remove(at: x)
print("刪除索引為4的字符后得到的字符串\(subStr)")

subStr.remove(at: subStr.startIndex)
print("刪除索引為0的字符后得到的字符串\(subStr)")

subStr.remove(at: subStr.index(after: subStr.startIndex))
print("刪除索引為0的字符后得到的字符串\(subStr)")

subStr.remove(at: subStr.index(before: subStr.endIndex))
print("刪除最后一個索引的字符后得到的字符串\(subStr)")


//刪除范圍字符串
let ran1 = subStr.index(subStr.endIndex, offsetBy: -3)..<subStr.endIndex
subStr.removeSubrange(ran1)
print("刪除從索引為倒數第3之后所有的字符串后得到的字符串\(subStr)")

let ran2 = subStr.index(subStr.startIndex, offsetBy: 4)..<subStr.endIndex
subStr.removeSubrange(ran2)
print("刪除從索引為4之后所有的字符串后得到的字符串\(subStr)")


subStr = "http://baidu.com"
let ran3 = subStr.range(of: ":")
let ran4 = subStr.range(of: ".")

subStr.removeSubrange((ran3?.upperBound)!..<(ran4?.lowerBound)!)
print("刪除從:開始到.之間的字符串后得到的字符串\(subStr)");

//字符串替換
subStr.replaceSubrange(ran3!, with: "wert")
print("ran3被wert替換之后的字符串\(subStr)");


//全部返回為布爾類型  Bool類型
//判斷字符串是否有前、后綴
p_str1.hasPrefix("3.0")
p_str1.hasSuffix("3.0")

//判斷字符串是否為空
p_str1.isEmpty
str3?.isEmpty
str4.isEmpty
str5.isEmpty


//--- 大小寫轉換 -----------
subStr = "ashdfasdjf"

print("subStr.capitalized(首字母大寫)方法:\(subStr.capitalized)")
print("subStr.uppercased()大寫方法:\(subStr.uppercased())")
print("subStr.lowercased()小寫方法:\(subStr.lowercased())")

//--- 字符串比較 -----------
subStr = "2.6.2"
p_str1 = "2.6.1"
str1 = "2.7"
str3 = "2.7.0"
subStr > p_str1
subStr > str1
str1 == str3
str1 > str3!
str1 < str3!

//--- 字符串轉換 -----------
Int(str1)
Double(str1)
Double(str3!)
Bool(str1)
str1 = "true"
Bool(str1)

3.控制語句

  • 分支語句 : if,switch和guard(新增)
    if后面的語句必須為bool類型,可以不加括號
    switch 可以支持Int,Float,Double,String,Tuple等AnyObject,無需break
    guard 是Swift2.x新增關鍵字,必須配合else使用,解決if-else多層嵌套問題,判斷一個條件為true下執行某語句,否則終止或跳轉執行其他語句

  • 循環語句 : while, repeat-while,for(改動),for-in
    for循環 摒棄掉了c的style,一律以...來替代,不得不說很贊。

for i in 1...arr.count{
      //循環體
}

swift專門提供了遍歷集合的方法for-in

let numbers = [1,2,3,4,5,6,7,8,9,10]
for (index,element) in numbers.enumerate(){
      print("Item \(index) : \(element)")     //index和item都有了
}

swift中的for循環提供了類似NSRange方式的跨步查找方式

for index in stride(from: 1, through: 5, by: 2) { print(index)}
  • 跳轉語句 : break,continue,fallthrough(新增),return,throw(新增)
    fall through 是貫通語句,只能在switch中使用,如果一個case執行完畢,需要執行另一個case,則需要用fallthough跳轉至下一個case
    throw 在oc中的NSError一定很熟悉了,在swift里,借助java的try..catch語句可以將程序中拋出(throw)的異常收集起來。

  • 值綁定 : 常用于if,guard,switch,where
    在控制語句中將表達式的值臨時賦給一個常量或者變量,這些常量或者變量可以在該控制語句中使用,稱為"值綁定"。
    where語句類似于sql中的where語句,非常好用。

//switch中使用元祖類型
var student = (id:"1002",name:"李四",age:32, ChineseScore:80, EnglishScore:89)
var desc: String
switch student {
case (_, _, _, 90...100, 90...100) where age > 20:  //where語句
    desc = "優"
case (_, _, _, 80...90, 80...90):
    desc = "良"
case (_, _, _, 60...80, 60...80):
    desc = "中"
case (_, _, _, 60...80, 90...100), (_,_, _, 90...100, 60...80):
    desc = "偏科"
case (_, _, _, 0...80, 90...100), (_,_, _, 90...100, 0...80):
    desc = "嚴重偏科"
default:
    desc = "無"
}
print("說明:\(desc)")

4.集合,函數,閉包

  • 集合,函數,閉包都是oc中常用的,就不多做介紹。
//Array
//swift中沒有提供二維數據,只有一維數組
var arr1 = [String]()
var arr2 = [Int]()
var arr3 = [AnyObject]()
var arr4 = ["1","2","3"]
arr1 = ["1","2","3"]
arr2 = [1,2,3]
arr3 = [1 as AnyObject,"2" as AnyObject,3 as AnyObject,4.4 as AnyObject]

arr1.first //第一個元素
arr1.last  //最后一個元素
arr1.contains("9")//是否存在該元素
arr2.contains(3)
arr1.count
let bool1 = arr1 == arr4 //兩數組是否相等
//增刪改查
arr1 += arr1
arr1.insert("9", at: 4)
arr1.append("10")
arr1.remove(at: 6)
arr1[0] = "123"
let bool2 = arr1 == arr4 //兩數組是否相等

for item in arr1 {
    print(item)
}
for (i,item) in arr1.enumerated() {
    if item == "2" {
        print(item)
        break
    }
}
//Dictionary
var dic1 = [String:Any]()
var dic2 = [Int:Int]()
let dic3 = [1:"4",2:"5",3:"6"]
let dic4 = ["2":"相同key值在合并的時候會替換","w":"5","e":dic3] as [String : Any]
dic1 = ["1":"qw","2":45,"3":UIView(),"4":dic3]
dic2 = [1:4,2:5,3:6]
print(dic1)
//遍歷
for value in dic1.values {
    if let val = value as? UIView {
        val.frame = CGRect(x: 10, y: 150, width: 250, height: 250)
    }
}
print(dic1)
for key in dic1.keys {
    print(key)
}
for (key,value) in dic1 {
    print("key:\(key) -- value:\(value)")
}
//增刪改查
dic1["增加"] = "增加的內容"
print(dic1)
dic1.removeValue(forKey: "4")
print(dic1)
dic1["2"] = 678
print(dic1)
(dic1["3"] as! UIView).frame = CGRect(x: 100, y: 100, width: 200, height: 200)
print(dic1)
//查詢
if let value = dic1["2"] {
    print("value =>\(value)" )
}else{
    print("value => 沒有" )
}
if let value = dic1["2"] as? String {
    print("value =>\(value)" )
}else{
    print("value => 沒有" )
}
if let value = dic1["22"] {
    print("value =>\(value)" )
}else{
    print("value => 沒有" )
}
if let value = dic1["22"] as? String {
    print("value =>\(value)" )
}else{
    print("value => 沒有" )
}
//----重載運算符 更方便實現兩個字典合并為一個字典
func += <key, value> ( left: inout Dictionary<key, value>, right: Dictionary<key, value>) {
    for (k, v) in right {
        left.updateValue(v, forKey: k)
    }
}
dic1 += dic4
print(dic1)
dic1 = dic4
print(dic1)
dic1.isEmpty
dic1.removeAll()
dic1.isEmpty
//函數
 func <#name#>(<#parameters#>) -> <#return type#> {
        <#function body#>
    }

5.其他

  • 恒等于(===)
    ===用于比較兩個引用是否為同一個實例

  • 不恒等于(!==)
    !== 只能用于引用類型,類的實例

  • 訪問級別
    public:可以訪問自己模塊中的任何public實體
    internal:只能訪問自己模塊的任何internal實體
    private:只能在當前源文件中使用的實體

  • 關鍵字
    lazy: 延時加載
    static: 靜態屬性
    override: 重寫
    final: final聲明的類不能被繼承,屬性,方法,下標不能被重寫
    //待補充

  • 構造與析構
    結構體和類創建實例需要進行一些初始化工作,init(),deinit()
    析構函數只適用于類,C++中析構函數用來釋放不再需要的內存資源,但是在oc和swift中內存管理采用引用計數(ARC)不需要析構函數釋放資源,但有一些資源清理的工作需要在deinit()中完成,例如關閉文件,關閉數據庫等

  • 類構造函數橫向代理
    在定義構造函數時可以通過其他構造函數來完成實例部分構造過程,叫做構造函數代理。
    由于類有繼承關系,類構造函數代理比較復雜,分為橫向代理和向上代理
    => 橫向代理發生在同一類內部,稱為便利構造函數(convenience initializer)
    => 向上代理發生在繼承情況下,在子類構造過程中要先調用父類構造函數,初始化父類存儲屬性,稱為制定構造函數(designated initializer)

class Rectangle{
  var width : Double
  var height: Double

  init(width: Double,height:Double){
    self.width = width
    self.height = height
  }

 convenience init(length: Double){
    self.init(W: length, H: height)
  }

}
  • 類繼承
    安全檢查1:指定構造函數必須保證其所在類的所有存儲屬性都初始化完成,之后才能向上調用父類構造函數代理
    安全檢查2:指定構造函數必須先向上調用父類構造函數代理,然后再為繼承的屬性設置新值,否則指定構造函數賦予的新值將被父類的構造函數覆蓋。
    安全檢查3:便利構造函數必須先調用同一類中的其他構造函數代理,然后再為任意屬性賦新值。
    安全檢查4:構造函數在第一階段構造完成之前不能調用實例方法,也不能讀取實例屬性,因為不能保證要訪問的實例屬性已被初始化。

  • 類型檢查
    is 操作符可以判斷一個實例是否是某個類的類型
    as 操作符僅用于向上轉型,也可以用于oc和swift底層類型相互轉換
    as! 和 as? 可選類型與非可選類型進行轉換
    any 和 anyObject any表示任何類型,anyObject 表示任何類的類型

  • 泛型
    泛型在代碼中可以定義一些可變的部分,在運行的時候指定,使用泛型可以最大限度的重用代碼和保護類型的安全,提高整體性能。swift中array,dictionary,set都是泛型集合。
    例如要定義一個函數來判斷兩個參數是否相等,int,string,double,float各定義一次,如果使用泛型函數來改造,就有

//單參數類型
func isEquals <T> (a : T ,b : T) -> Bool{
    return (a == b)
}

//多參數類型
func isEquals <T , U> (a : T ,b: U) -> Bool{
   return (a == b)
}


二、進階篇

.......................................

1.值類型/引用類型

  • swift中只有類是引用類型,其他類型全部是值類型。
  • 傳遞參數時要拷貝一份。例如常用的結構體和枚舉都是值類型。

2.屬性和變量(propertry,variable)

  • 關于swift中屬性和變量的描述,有一個兄弟總結的比較好,請到 傳送門

在OC中使用屬性一般都使用如下格式:

@property (strong,nonatomic) NSString *string;

而在Swift中的屬性一般都使用如下格式:

class Shape { var name = "shape"}

在oc中可以直接與變量打交道,swift中則行不通,在Swift的世界里,我們只與property打交道。在Swift中,屬性分為存儲屬性和計算屬性。簡單來說,就是存儲屬性能夠保存值,而計算屬性只提供getter與setter,利用存儲屬性來生成自己的值。計算屬性更像方法,而不是傳統意義的屬性。但是這樣一個特性存在,使得我們更容易組織我們的代碼。

構造變量
  • 在OC中,只能通過單例,或者static變量加類方法來構造類變量:
@interface Model

+ (int) value;
+ (void) setValue:(int)val;

@end

@implementation Modelstatic 

  int value;  
+ (int) value{ 

@synchronized(self) { 
  return value; 
} 

+ (void) setValue:(int)val{ 
  @synchronized(self) { 
    value = val; 
    } 
}
@end

// Foo.h@interface Foo {}
+ (NSDictionary *) dictionary;
// Foo.m oc單例
+ (NSDictionary *) dictionary{ 
  static NSDictionary* fooDict = nil; 
  static dispatch_once_t oncePredicate; 
  dispatch_once(&oncePredicate, ^{ 
  // create dict 
  }); 
  return fooDict;
}
  • 在swift中,可以通過清晰的語法便能定義類變量,通過static定義的類變量無法在子類重寫,通過class定義的類變量則可在子類重寫。
struct SomeStructure { 
  static var storedTypeProperty = "Some value." 
  static var computedTypeProperty: Int { 
    return 1 
  } 
  class var overrideableComputedTypeProperty: Int { 
    return 107 
  }
}

//swift單例
class singletonClass { 
  static let sharedInstance = singletonClass() 
  private init() {}
  // 這就阻止其他對象使用這個類的默認的'()'初始化方法
}

計算屬性
  • getter 訪問器 取出屬性值
  • setter 訪問器 給屬性賦值計算
    只讀計算屬性省略set()代碼,只保留get()代碼
存儲屬性

待補充

屬性觀察者
  • 屬性觀察者 能夠監聽存儲屬性的變化,但不能監聽延遲存儲屬性和常量屬性,常常應用于后臺處理,需要更新界面的業務需求。類似于OC中的KVO機制。
  • willSet 觀察者在修改之前調用
  • didSet 觀察者在修改之后立刻調用 默認newValue和oldValue
class Employee{
  var no : Int = 0
  var name : String = "test"{
    willSet(newName){
      print("員工name新值 : \(newName)")
    }
    didSet(oldName){
      print("員工name舊值 : \(oldName)")
    }
  }
}

3.擴展(Extension)/協議(Protocol)

  • 拓展
    在swift和oc中,可以使用一種擴展機制,在原始類型(類,結構體,枚舉)的基礎上添加新功能,是一種“輕量級”的繼承機制。擴展類型可以是類,結構體,枚舉,而繼承只能是類。常用的擴展可以擴展基本類型,方法,構造函數等等。
extension 類型名{
    //添加新功能
}
  • 協議
    協議是swift和oc中的名稱,在Java中稱為接口,在C++中是純虛類,協議是高度抽象的,規定一些方法名,參數列表,返回值等信息,但是不給出具體的實現,這種抽象方法由遵從該協議的遵從者自己內部實現,具體實現過程稱為遵從協議或者實現協議。
    在結構體和枚舉類型中可以定義變異方法,在類中沒有該方法,因為結構體和枚舉類型中的屬性是不可以被修改的,在協議定義變異方法時候,前面要加關鍵字mutating
    swift和oc又被稱為面向協議編程語言,在面向協議編程中(Java中稱為面向接口編程)應用的定義與實現進行分離,協議作為數據類型暴露給使用者,不關心具體的實現細節,從而提供應用的可擴展性和可復用性。
protocol leader{
  mutating func method()  //定義抽象方法
}
class member : leader {
  func method()  // 實現具體實例方法
} 
struct member_str : leader{
  mutating func method()    //實現變異方法
}

在Objective-C中我們這么聲明Protocol:

@protocol SampleProtocol <NSObject>
- (void)someMethod;
@end

而在Swift中:

protocol SampleProtocol { func someMethod() }

在Swift遵循協議:

class AnotherClass: SomeSuperClass, SampleProtocol{ func someMethod() { } }

4.內存管理

swift和oc的內存管理都是采用了自動引用計數(ARC),很早之前的oc還是采用比較古老的手動引用計數(MRC),swfit在這方面有著基因級別的優勢。ARC就是程序員不關心對象釋放的問題,編譯器在編譯時在合適的位置插入對象內存釋放代碼。引用計數具體細節這里就不詳細說明了。
但是采用ARC會導致強引用循環,即當兩個對象的存儲屬性互相引用對方的時候,一個對象釋放的前提是對方先釋放,另一個對象釋放的前提也是對方先釋放,就會導致死鎖的狀態,從而導致內存泄露,打破強引用循環一般采用弱引用或者無主引用,使用weak和unowned 或可選類型(Optional)

//Object-C
weak self

//swift
weak var dept: Department?

5.錯誤處理

  • Cocoa錯誤處理
    首先定義一個NSError變量,然后判斷err變量是否為空
NSError *anyError;
BOOL success = [receivedData writeToURL:someLocalFileURL options:0 error:&anyError];
if (!success) {
NSLog(@”Write failed with error: %@”, anyError);
// present error to user
}
  • swift錯誤處理
    由于歷史原因swift1.x中錯誤處理模式沿用了Cocoa框架的錯誤處理模式,swift2.x和swift3.x采用了do-try-catch錯誤處理模式,在使用try時候經常也會遇到try?和try!
    try?會將錯誤轉換為可選值,當發生錯誤拋出異常時,程序不會崩潰,而是反饋一個nil,try!可以打破錯誤傳播鏈條,終止錯誤傳遞。
try {
     //成功處理語句
}catch{
     //錯誤處理語句
}

6. Id和AnyObject 以及Optional

在Swift中,沒有id類型,Swift用一個名字叫AnyObject的protocol來代表任意類型的對象。

id myObject = [[UITableViewCell alloc]init];
var myObject: AnyObject = UITableViewCell()

我們知道id的類型直到運行時才能被確定,如果我們向一個對象發送一條不能響應的消息,就會導致crash。我們可以利用Swift的語法特性來防止這樣的錯誤:

myObject.method?()

如果myObject沒有這個方法,就不會執行,類似檢查delegate是否有實現代理方法。在Swift中,在AnyObject上獲取的property都是optional的。

7.函數式編程/block閉包

  • 函數
    在OC中,方法有兩種類型,類方法與實例方法。方法的組成由方法名,參數,返回值組成。
+(void)類方法
-(void)實例方法
+(UIColor*)blackColor    //類方法
-(void)addSubview:(UIView *)view   //實例方法 
class func blackColor() -> UIColor   //類方法
func addSubview(view: UIView)      //實例方法
//OC
NSArray *oldArray = @[@1,@2,@3,@4,@5,@6,@7,@8,@9,@10]; 
NSMutableArray *newArray; 
for (NSNumber* number in oldArray) { 
  if ([number compare:@4] == NSOrderedDescending ) { 
  [newArray addObject:number]; 
  } 
}

//swift函數式編程
let oldArray = [1,2,3,4,5,6,7,8,9,10]
let newArray = oldArray.filter({$0 > 4})

在Swift中,函數的最重要的改進就是函數作為一等公民(first-class),和對象一樣可以作為參數進行傳遞,可以作為返回值,函數式編程也成為了Swift支持的編程范式。

  • block閉包
    OC中的block在Swift中無縫地轉換為閉包。函數實際上也是一種特殊的閉包。
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success) { 
  NSLog(@"Error: %@", error.domain);
}

//do-try-catch
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("/path/to/file")
do { 
  try fileManager.removeItemAtURL(URL)
} catch let error as NSError { 
  print("Error: \(error.domain)")
}

8.KVO

  • Swift支持KVO。但是KVO在Swift,個人覺得是不夠優雅的,KVO在Swift只限支持繼承NSObject的類,有其局限性,在這里就不介紹如何使用了。網上也出現了一些開源庫來解決這樣的問題。也可以使用前文講述的屬性觀察者來做Swift的KVO機制。
  • KVO 在OS X中有Binding的能力,也就是我們能夠將兩個屬性綁定在一起,一個屬性變化,另外一個屬性也會變化。對與UI和數據的同步更新很有幫助,也是MVVM架構的需求之一。之前已經眼饞這個特性很久了,雖然Swift沒有原生帶來支持,Swift支持的泛型編程給開源界帶來許多新的想法。

三、實戰篇

很想寫點什么實戰類型的東西,但是坦誠來說,筆者并沒有太多的實戰經驗,在這里僅總結一些前人的經驗以供參考,后續可以繼續補充。這里CSDN上的一篇寫的很好了 可以參考一下 => 傳送門

1.Swift與OC的常見區別
  • swift句尾不需要分號 ,除非你想在一行中寫三行代碼就加分號隔開。

  • swift不要寫main函數 ,程序默認從上往下執行

  • swift不分.h和.m文件 ,一個類只有.swift一個文件

  • swift不在有地址的概念

  • swift數據類型都會自動判斷 , 只區分變量var 和常量let

  • 強制轉換格式反過來了 OC強轉:(int)a Swift強轉:int(a)

  • 整數的數據類型可以通過 .min和.max獲得最大和最小值

  • 定義類型的別名語法改變

//OC:
typedef int MyInt 
//Swift:
typealias MyInt = int
  • swift的模除取余運算符支持小數了 。 如 5%1.5 = 0.5

  • 關于BOOL類型更加嚴格 ,Swift不再是OC的非0就是真,而是true才是真false才是假

  • swift的賦值運算符沒有返回值 。防止誤用“=”和“==”

  • swift可以多對多賦值,即元祖類型 。 let(x,y) = (1,2)

  • swift的 循環語句中必須加{} 就算只有一行代碼也必須要加

  • swift的switch語句后面以前只能跟整數, 現在可以跟各種數據類型了 ,如浮點字符串都行,并且里面不用寫break,如果不想要沒break的效果 即后面的都想執行 那就寫上關鍵字 fallthrough(注意:在fallthrough后面就不能再定義常量變量了)

2.控件類
  • 初始化UIView的子類
    在iOS應用上實現UI就需要子類化UIView,也就是要重寫UIView的init方法。注意:兩種語言有所區別。 Objective-C只需在UIView子類中重寫必要的init方法。要初始化一個UIView框架,就要重寫initWithFrame:框架,如下所示:
@implementation SubUIView
- (id) initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self != nil) {
        // ...
    }
    return self;
}
@end
  • 然而Swift需要多一些步驟來重寫同一個init方法。首先,重寫使用CGRect框架作為其參數的init方法。根據UIView文檔,用Swift語言構建時,須重寫init( coder: ),但我們不需要這種方法,就用如下代碼處理。類屬性初始化所需的代碼可以在init( frame: )中執行。
class SubUIView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        // ...
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
  • 初始化UIViewController的子類
    子類化UIViewController是iOS開發的重要步驟。使用Interface Builder的開發者需要重寫initWithNibName:bundle:,但既然我們用代碼來構建UI,就不需要執行這一方法了。只需重寫init方法,在其中初始化類屬性即可。
@implementation SubUIViewController
- (id) init
{
    self = [super init];
    if (self != nil) {
        // ...
    }
    return self;
}
@end
  • Swift也一樣要重寫init()方法。實現指定的初始化init(nibName:bundle: )來子類化UIViewController。重申:此過程不適用Interface Builder,所以無需定義nibName和bundle的值,而是調用比指定初始化更簡單的convenience初始化,將指定初始化init(nibName:bundle: )設為零。現在調用init()來初始化類,用重寫的(nibName:bundle: )執行類屬性。
class SubUIViewController: UIViewController {
    convenience init() {
        self.init(nibName: nil, bundle: nil)
    }
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        // Initialize properties of class
    }  
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

//現在可以創建和調用UIViewController的子類
let viewController: SubUIViewController = SubUIViewController()
self.navigationController?.pushViewController(viewController, animated: false)

3.Auto Layout
4.選擇器

使用UIButton、NSNotificationCenter、NSTimer等時,使用選擇器來分配要執行的方法。在Objective-C中,@selector指令代表使用選擇器。

- (void)test
{
    // ...
    mTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerCallback:) userInfo:nil repeats:YES];
}

- (void)timerCallback:(NSTimer *)timer
{
    // ...
}

Swift中,不需要使用指令或字符串來分配方法。

        let button = UIButton(type: UIButtonType.system)
        button.setTitle("OK", for: UIControlState())
        
        let buttonWidth:CGFloat = 60
        let buttonHeight:CGFloat = 20
        let buttonTopView:CGFloat = 240
        
        button.frame = CGRect(x: (screen.size.width - buttonWidth)/2 , y: buttonTopView, width: buttonWidth, height: buttonHeight)
        
        button.addTarget(self, action: #selector(ViewController.onClick(_:)), for: UIControlEvents.touchUpInside)
        
        self.view.addSubview(button)

四、總結

.............................

事實上Swift的世界相比OC的世界還有很多新鮮的東西等待我們去發現和總結,Swift帶來的多范式編程也將給我們編程的架構和代碼的組織帶來更來的思考。而Swift也是一個不斷變化,不斷革新的語言。相信未來的發展和穩定性會更讓我們驚喜。

  • PS:谷歌醞釀將蘋果Swift作為安卓APP主要開發語言
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容