电一闪的博客

平凡亦有极致体现

Swift学习笔记

| Comments

背景

  swift2.0本月刚刚发布,在1.0基础上,增加了些许新特性,改变了1.0中少量语法。

Swift 1.0理解畅谈

一、优势

  1、if x = y是错误代码,在Swift里会报编译错。
  2、溢出运算符、运算符重载。
  3、swift可以对浮点数取余 8%2.5结果为0.5。
  4、OC时代

return str1 != nil ? str1 : defaultStr

  现在swift里可以简化为

return str1 ?? defaultStr。  

  5、Unicode字符标量方便表示,比如 let oneUniChar = “\u{2665}”。
  6、作为基础类型之一的String,其赋值时可视作深拷贝(实际大多数情况下不是),又经swift编译器优化,在必要时才会深拷贝,以提高性能。
  7、向数组中加入元素可以直接用运算符+=,前提是保证被加入的元素与数组本身的元素类型保持一致。
  8、可以方便地将数组中某一范围的元素,替换为另一数组,且前后数量可以不匹配。例如

var elements = ["lym1", "lym2", "lym3"]
elements[1...2] = ["lym4"]

  这样数组elements就被修改为[“lym1”, “lym4”]。
  9、for-in循环支持区间、序列、集合、系列遍历,使用_可以方便控制只循环次数且不对值访问。
  10、使用switch不再需要break语句,且case的条件判断可以使用if语句样式的条件表达式(例如区间、元组、字符串),因此原来各种条件判断、计算结果非整型的表达式,现在都可以使用switch来提高判断效率。
  11、switch语句默认不再有贯穿case的做法(case之间不写break),而是显式地使用fallthrough关键字,不丢弃贯穿特性,同时意图更加清晰。
  12、使用标签标注“循环”或“switch代码段”,方便地跳出多重循环(第一次了解这个概念是在学习java时,可以说是借鉴)。
  13、swift中简化了函数重载,C++中函数重载需要多次函数定义,而OC原本不支持函数重载,现swift使用函数默认参数值即可在一次函数定义中实现函数重载。例如,方法定义:

func testOverrideMethod(value1:Int16, value2:Int16 = 2) {
    print("testOverrideMethod, value1:\(value1), value2:\(value2)")
}  
testOverrideMethod(1, value2: 22)  

  如果给参数设置了默认值,那么调用方法时必须指明参数名;否则调用时可以不用写名”value2:“。
  14、swift中函数传值时,如果外部对象真正的值想在函数体中被修改,使用inout关键字修饰变量,用以替代指针的指针这样的复杂概念“**”,例如:

func swap (inout value1 : Int16, inout value2 : Int16) {
    var tempValue = value1
    value1 = value2
    value2 = tempValue
}
var value1 = 1, value2 = 2
//打印结果"value1:1, value2:2"
print("value1:\(value1), value2:\(value2)") 
swap(&value1, &value2)
//打印结果"value1:2, value2:1"
print("value1:\(value1), value2:\(value2)")

  15、swift中的枚举可支持任意类型,包括但不限于Int、String、Bool、Tuple(元组),且可以在一个枚举定义中支持任意多种成员类型。
  16、swift拥有“可失败构造器”。在OC中,常常碰到先alloc分配内存,再initWithXXX传递参数初始化,可在某些条件下(比如参数类型错误)无法初始化有意义的对象,为instance分配内存就造成了时间空间浪费。具体做法是,swift类构造器的init关键字后紧跟问号,方法定义写成init?(xxx),在满足失败条件时,返回nil。
  17、与OC同时期的C++早就有了泛型的概念,而OC一直缺乏,现在swift补上了这一空缺。

二、陷阱

  1、使用countElements计算字符串长度是基于unicode,而NSString的length方法则是基于UTF-16,因此NSString的length属性在被swift的String访问时,会成为utf16count。
  2、在swift中构造的类,OC中无法继承。
  3、为了避免循环引用,在OC中导入swift类,需要在.h文件中用@class声明,.m文件中导入。
  4、swift用于判断两个类实例对象是否为同一对象的运算符为”===“和”!==“,分别名为“等价于”和“不等价于”。而实例具体内容是否相等需要根据类中定义的“等于”(==)方法判定,该方法属运算符重载。
  5、swift中的String、Array、Dictionary类型均为结构体类型,赋值时为深拷贝赋值;不同于OC中NSString、NSArray、NSDictionary的浅拷贝,即复制引用。不过swift会在后台完成性能优化,以确保在有需要时才做深拷贝。
  6、swift的构造器,构造顺序与OC相反。OC中必须先调用父类初始化方法,才能初始化子类变量;而swift必须先给子类未初始化的变量赋值,再调用父类指定构造器(Desinated)。其本质原因为,swift加强了构造安全,目的是准确分配内存。具体步骤为:

a. 初始化子类中所有引入的存储型属性 
b. 调用父类指定构造器。按此步骤,直到祖先类最顶端的指定构造器调用完成,才算子类实例完全初始化,此时self成为一个有效的引用型变量
c. 自顶向下,让各个子类有机会修改继承来的存储型变量。

  反观OC,在子类初始化时,自下向上调用初始化方法,先使self成为有效对象,再为子类变量分配内存。无所谓孰优孰劣,只是swift在构造器的安全性上略有提升。
  7、在定义子类时,想要继承父类所有构造器,必须没有定义任何子类构造函数;或子类覆盖实现了所有父类指定构造器(Desinated)。这样设计的目的,个人猜想是为了让子类更纯粹,不暴露无关的父类构造器以致混淆类定义,强化类的目的性。
  8、便利构造器(Convenience)不可继承,且实现中必须以调用指定构造器结束(Desinated)。它在类中起辅助作用,目的是减少构造器中的重复代码。
  9、swift中的前缀运算符与后缀运算符,必须紧跟变量,中间不能插空格。例如,OC中常写i ++,但在swift中必须i++。

三、与OC对比

  1、swift有简单明了的函数声明。首先,它有内部参数名,在调用时会被当作占位符。

  这一块乍看起来像JS,但仔细观察,会发现一些OC的影子。比如倒数第二行带占位符的函数调用,参数名后跟“:value”,是典型的OC风格。 不仅于此,为了防止代码编写完成后由于没有内部参数名而难以阅读,swift还增加了外部参数名机制。代码如下

  ”from”和”say”作为外部参数名,用于显式描述参数用途。另,在内部参数名前使用#标注,即可同时作为内外部参数名。
  2、我觉得,swift彻底把“方法”(函数)变成了“block”(命名闭包)。比如这段代码:

  根据传入值的true or false,返回在函数内部定义的函数变量。使用OC的block实现如下,是否有异曲同工之妙?

  3、swift的尾随闭包非常简便易用,但简写的方法非常跳跃。(小屏请横屏查看)

代码:

//闭包作为函数的最后一个参数时,可以改写为尾随闭包
//定义一个函数,其参数是一个类型为Int64,返回值为Void的闭包closure
func someFuncForTestClosure (closure:(Int64) -> Void) {
    closure(4)
}
//调用已定义的函数,并定义闭包作为参数
someFuncForTestClosure ({ (para) -> Void in
    print("调用了closure,参数为\(para)")
    print("调用了closure,参数为\(para)")
})
//如果闭包特别复杂特别长,尾随闭包作用凸显
someFuncForTestClosure (){ (para) -> Void in
    print("调用了closure,参数为\(para)")
    print("调用了closure,参数为\(para)")
    print("调用了closure,参数为\(para)")
    print("调用了closure,参数为\(para)")
    print("调用了closure,参数为\(para)")
}
//尾随闭包前的函数参数括号也可以省略
//不理解缩写过程咋一看还以为是函数定义
someFuncForTestClosure { (para) -> Void in
    print("调用了closure,参数为\(para)")
    print("调用了closure,参数为\(para)")
}
//简直丧心病狂,还可以简写为
someFuncForTestClosure { 
    print("调用了closure,参数为\($0)")
    print("调用了closure,参数为\($0)")
}

  4、swift中,可变类型和常量类型用关键字声明区分,var代表变量,let代表常量。而原来OC中使用两种类NSXXX和NSMutableXXX定义是否可变。

  5、swift三种元类型在修改self值时的区别。其中class修改self或实例属性最简单;struct需要将关键字mutating作为方法前缀才能实现;enum枚举无法实现,因其无法定义存储型变量,只能定义case。

//class类
class Point {
    var x = 0.0, y = 0.0
    //class中的函数,对self的值想怎么改就怎么改
    func moveByX(deltaX: Double, y deltaY: Double) {
        self.x += deltaX
        self.y += deltaY
    }
    init(x:Double, y:Double) {
        self.x = x
        self.y = y
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
println("The point is now at (\(somePoint.x), \(somePoint.y))")
// 输出 "The point is now at (3.0, 4.0)"

//struct结构体
struct Point_struct {
    var x = 0.0, y = 0.0
    //要修改self的值,必须在声明前添加mutating关键字
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        self.x += deltaX
        self.y += deltaY
    }
    init(x:Double, y:Double) {
        self.x = x
        self.y = y
    }
}
var somePoint_struct = Point_struct(x: 1.0, y: 1.0)
somePoint_struct.moveByX(2.0, y: 3.0)
println("The point is now at (\(somePoint_struct.x), \(somePoint_struct.y))")
// 输出 "The point is now at (3.0, 4.0)"

//枚举,无法实现
enum Point_enum {
    //报错:枚举定义不能包含存储属性
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        self.x += deltaX
        self.y += deltaY
    }
    init(x:Double, y:Double) {
        self.x = x
        self.y = y
    }
}

Comments