swift

阅读Swifter - 100 个 Swift 必备 tips(

2015-04-04  本文已影响2133人  小白和小黑

开始阅读《Swifter - 100 个 Swift 必备 tips》,一边学习一边记录,一边打代码 ,一边提问。因为Swift是新出的,所以作者还是先讲述OC知识然后转换为Swift,但是我属于先学Swift那种人,所以得先写Swift后写OC


1.Selector
直接使用了Selector

 let NameSEL = Selector("setAge:blog:")

因为Selector实现了StringLiteralConvertible,可以不用初始化,代码如下

NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: "setAge:blog:", userInfo: nil, repeats: false)
func setAge(age:Int,blog:NSString){
    //...
}

这里还有一个问题就是IOS runtime 我会在后面写篇文章整理一下。

如果方法由private修饰的,正确代码如下(前面需要加@objc)

NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: "setAge:blog:", userInfo: nil, repeats: false)
@objc private func setAge(age:Int,blog:NSString){
    //...
}

若方法第一个参数是外部参数,正确代码如下(方法名with参数名)

func set(exman name:NSString){...}
let s = Selector("setWithExman:")

2.柯里化
作为小白的我看到这三个字我无语了,神马意思,来吧,找网络。

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。

Ole Begemann 使用了一个简单的代码简单介绍一下柯里化Demo.

class BankAccount {
    var balance: Double = 0.0
    func deposit(amount: Double) {
           balance += amount
}

}
我们调用一般是初始化(差点说了new其实差不多的说法但是没有new)

let account = BankAccount()
account.deposit(100)//balance is 100

利用柯里化代码如下

let depositor = BankAccount.deposit
depositor(account)(100) //balance is 200

是不是觉得很别扭,其实很简单,一开始我觉得这是什么,难道不会抱错,其实就是你怎么看返回值而已

let depositor:BankAccount->(Double)->()//相当于后面那个是返回值,因为可以返回一个函数,这个函数参数是Double类型,无返回值。

应用到代码最好的地方就是更改Selector的问题,因为Selector只接受String类型的参数,其他的都不能接收。用柯里化优化代码如下

protocol TargetAction {
     func performAction()
}
struct TargetActionWrapper<T: AnyObject> : TargetAction {
     weak var target: T?
     let action: (T) -> () -> ()
    func performAction() -> () {
          if let t = target {
                action(t)()
         }
    }
 }
enum ControlEvent {
    case TouchUpInside
    case ValueChanged
    // ...
}

 class Control {
        var actions = [ControlEvent: TargetAction]()
        func setTarget<T: AnyObject>(target: T, action: (T) -> () -> (), controlEvent: ControlEvent) {
                    actions[controlEvent] = TargetActionWrapper(target: target, action: action)
}

func removeTargetForControlEvent(controlEvent: ControlEvent) {
    actions[controlEvent] = nil
}

func performActionForControlEvent(controlEvent: ControlEvent) {
    actions[controlEvent]?.performAction()
       }
 }

调用

class MyViewController {
       let button = Control()
       func viewDidLoad() {
            button.setTarget(self, action: MyViewController.onButtonTap, controlEvent: .TouchUpInside)
}
func onButtonTap() {
    println("Button was tapped")
      }
}

试试确实很棒,但是我还需要时间消耗,而且我也好奇我怎么能打出代码不看这些资料。


3.protocol 的方法声明为 mutating

protocol Vehicle
{
   var numberOfWheels: Int {get}
   var color: UIColor {get set}
   mutating func changeColor()
}
struct MyCar: Vehicle {
   let numberOfWheels = 4
   var color = UIColor.blueColor()
   mutating func changeColor() {
            color = UIColor.redColor()
 } }

4.Sequence
序列化是内部自动实现的,在程序中我们能很好使用for-in的原因是这个类一定是实现了SequenceType,书里面如果我们自己写一个能进行序列化的类,得先实现GeneratorType协议

书里面介绍了实现了SequenceType还有其他的方法.

  func map<S : SequenceType, T>(source: S,
        transform: (S.Generator.Element) -> T) -> [T]
  func filter<S : SequenceType>(source: S,
        includeElement: (S.Generator.Element) -> Bool) -> [S.Generator.Element]
  func reduce<S : SequenceType, U>(sequence: S,
        initial: U, combine: (U, S.Generator.Element) -> U) -> U

我都以数组为例

var arr = [1,2,3,4,5]
map:
arr.map { $0/2 }//[0,1,1,2,2]
filter:
arr.filter({ $0%2 == 0 })//[2,4]
reduce://第一个参数代表运算几次
arr.reduce(2, combine: { $0 * $1
})//240


5.多元组 (Tuple)
说实话你看完这个代码,你会笑死真的,为什么早不这么写代码呢,太小清新了。

func swapAB<T>(inout a:T,inout b:T){
     (a,b) = (b,a)
 }

利用元组瞬间完成交换.是不是惊呆了,我当时也是,不用中间变量了.
但是看第二个例子我就觉得没第一个惊喜,因为作者在OC已经习惯了在需要错误处理做一个 NSError 的指针,然后将地址传到方法里等待填充。直接上代码

 func doSomethingMightCauseError() -> (Bool, NSError?) { //... 做某些操作,成功结果放在 success 中
     if success {
          return (true, nil) } else {
          return (false, NSError(domain:"SomeErrorDomain", code:1, userInfo: nil))
   } }
  let (success, maybeError) = doSomethingMightCauseError() 
  if let error = maybeError {
          // 发生了错误 }

好吧,彩蛋是一个接着一个呀。作者竟然发现了一个很神奇的东西,我搞不懂这个是Apple公司故意还是为未来打算的

测试代码如下:(你发现什么了)

  var num = 42
  println(num)
  println(num.0.0.0.0.0.0.0.0.0.0)

神奇的地方就是,每个对象都是一个元组,现阶段没发现这样做会出现什么问题,但是很好玩。这就是编程的乐趣吧,发现各种菜单,解决各种bug,说不定未来bug就发生在这里的。


6.@autoclosure 和??
在swift中有一个概念是闭包,就是其他代码的代码块,一样的道理。

func setName(myName : ()->Bool){
   if myName() {
      println("true")
   }
}
setName {2>1}

使用@autoclosure 代码如下

func setName2(getName:@autoclosure()->Bool){
    if getName(){
       println("true")
    }
}
setName2(2>1)

??:运算符
规则:当前左侧为nil时,返回右侧的值;当左侧不为nil时,返回其值。定义如下:

  func ??<T>(optional: T?, @autoclosure defaultValue: () -> T?) -> T?
  func ??<T>(optional: T?, @autoclosure defaultValue: () -> T) -> T

7.Optional Chaining
还是拿书上的代码做演示

 class Toy {
    let name: String
    init(name: String) {
       self.name = name
   }
}
class Pet {
    var toy: Toy?
}
class Child {
    var pet: Pet?
}

获取xiaoming的宠物玩具的名字的时候如下

var name = xiaoming.Pet?.Toy?.name//可能为nil相当一条锁链
改为如下
if let name = xiaoming.Pet?.Toy?.name{...}

如果做一个闭包方便调用,代码如下:

extension Toy{
func play(){
         //......
  }
}
var xiaohuang = Child()
let Closure = {(child:Child)->()? in child.pet?.toy?.play()}
if let Children = Closure(xiaohuang){
    //小黄的宠物玩具玩游戏
}

8.操作符
这节让我又学了Swift一个知识点,感觉应该是我之前看文档的时候,漏掉了,没事,现在捡起来。

运算符函数

定义了一个名为Vector2D的二维坐标向量(x,y) 的结构,然后定义了让两个Vector2D的对象相加的运算符函数。

struct Vector2D {
    var x = 0.0, y = 0.0
}

最神奇的一段代码,好吧,因为我之前没看到,所以觉得神奇:

func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
}

当你要定义一个不属于库中的操作符的时候, 需要设置这个操作符的一些属性,代码如下:

func +* (left: Vector2D, right: Vector2D) -> Double { 
         return left.x * right.x + left.y * right.y
}
infix operator +* {
      associativity none
      precedence 160
}

infix
      表示要定义的是一个中位操作符,即前后都是输入;其他的修饰子还包括 prefixpostfix,不再赘述;
associativity
      定义了结合律,即如果多个同类的操作符顺序出现的计算顺序。比如常见的加法 和减法都是 left,就是说多个加法同时出现时按照从左往右的顺序计算 (因为加法满足交换律,所以这个顺序无所谓,但是减法的话计算顺序就很重要了)。点乘的结果是一个 Double,不再会和其他点乘结合使用,所以这里写成 none;
precedence
      运算的优先级,越高的话越优先进行运算。Swift 中乘法和除法的优先级是 150, 加法和减法是 140,这里我们定义点积优先级 160,就是说应该早于普通的乘除进 行运算。


9.func 的参数修饰

主要需要注意当参数修饰符为inout和一般的修饰符在改变值的区别

var x = 1 
func setX(inout myX:Int){
     ++myx
}
setX(x)//x值改变了为2

10.方法参数名称省略

参数名前通过加#_显示参数名称或者隐藏参数名称,一般情况下,第一个参数都会被省略。有一个特殊的案例,就是全局的方法,所有参数名可以全部省略。


11.Swift 命令行工具
基本上从我学Swift时候开始,一直在使用Xcode,听说过控制台,但是没听说过有命令行工具.
脱离Xcode编程生成二进制文件->使用Swiftc进行编译

注:Swift 的命令行工具还有不少强大的功能,对此感兴趣的读者不妨使用 xcrun swift --help 以􏰀 xcrun swiftc --help 来查看具体还有哪些参数可以使用。


12.字面量转换
Swift提供了字面量的接口。用于将字面量转换为特殊的特殊的类型。

    • ArrayLiteralConvertible
    • BooleanLiteralConvertible
    • DictionaryLiteralConvertible
    • FloatLiteralConvertible
    • NilLiteralConvertible
    • IntegerLiteralConvertible
    • StringLiteralConvertible

举个例子:

class Person: StringLiteralConvertible {
    let name: String
    init(name value: String) {
         self.name = value
 }
   required convenience init(stringLiteral value: String) {
         self.init(name: value)
}
   required convenience init(extendedGraphemeClusterLiteral value: String) {
         self.init(name: value)
}
   required convenience init(unicodeScalarLiteral value: String) {
         self.init(name: value)
}

let p:Person = "xiaoming"//赋予Person拥有String的特性,String转换为Person类
println(p.name)

注:在 extension 中,我们是不能定义 required 的初始化方法的


13.下标
对于系统提供的下标无法满足你的时候,你需要自己做扩展,虽然下面是书中的代码,作者还不推荐这种方式,好吧, 原来是因为参数列表的原因

extension Array {
     subscript(input: [Int]) -> Slice<T> {
       get {
              var result = Slice<T>() for i in input {
                  assert(i < self.count, "Index out of range")
                  result.append(self[i]) }
                  return result
           }set {
        for (index,i) in enumerate(input) {
               assert(i < self.count, "Index out of range")
               self[i] = newValue[index]
            } }
     }

14.方法嵌套

Swift 提供了 public,internal 和 private 三种访问权限,可以采用方法嵌套就能很好的管理好权限的问题。


15.实例方法的动态调用
先看一下实例方法的代码

 class MyClass {
          func method(number: Int) -> Int {
                   return number + 1 }
         class func method(number: Int) -> Int { return number
  } }

没错,你没看错,里面的那个class func method是实例方法


16.命名空间
我的大脑印象中,好像C#里面有命名空间的说法,但是作用是什么,不清楚。作者是这么说的

Swift 的命名空间是基于 module 而不是在代码中显式地指明,每个 module 代表了 Swift 中的一个命名空间。也就是说,同一个 target 里的类型名称还是不能相同的。在 我们进行 app 开发时,默认添加到 app 的主 target 的内容都是处于同一个命名空间中的, 我们可以通过创建 Cocoa (Touch) Framework 的 target 的方法来新建一个 module,这样 我们就可以在两个不同的 target 中添加同样名字的类型了

//使用的时候语法
module(target).ClassName.FunctionName(或者属性)

好吧这就是一种宣传的感觉,没有太多的实际意义,对了,作者也介绍了还可以使用结构体。

说明一点,代码以及引用版权在于书的作者,我在总结,我现在还属于小白阶段,得向人学习,但是我觉得简书的互动性不好,为什么只有喜欢文章,基本不会去评论,还是说只是把我整理的文章放进去了,就成为古董再也不看了。希望互动起来,虽然我是小白,还是希望和大家交流,说不定会有意想不到的收获。书还会接着读下去,也不可以一下子全读完,我还得去思考,去实践,所以我把这个系列做完整。谢谢大家的眼睛。

上一篇 下一篇

猜你喜欢

热点阅读