Swift与OC详谈

作者: earthX | 来源:发表于2016-11-15 14:22 被阅读363次

    最近瞄上了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主要开发语言

    相关文章

      网友评论

      本文标题:Swift与OC详谈

      本文链接:https://www.haomeiwen.com/subject/zgpqpttx.html