美文网首页swift
阅读Swifter - 100 个 Swift 必备 tips(

阅读Swifter - 100 个 Swift 必备 tips(

作者: 小白和小黑 | 来源:发表于2015-04-04 11:57 被阅读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(或者属性)

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

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

    相关文章

      网友评论

      • Twenty_:能把PDF发给我吗?我在找这个文档
      • angelen:我想问一下,你在柯里化那里写到:
        let button = Control()
        那假如我的button是从Storyboard那里connect过来的话,它的定义将会是
        @IBOutlet weak var button: UIButton!
        这个时候我应该怎么使用呢?
        angelen:@小白和小黑 我是已经在Storyborad那里把那个“按我~”按钮设置class为那个Control了
        angelen:@小白和小黑 设置了继承的话,会报如下的错误:
        详细如下图:
        http://7u2ogo.com1.z0.glb.clouddn.com/problem-8A3D7508-43F9-4528-A2BA-F0B12690C490.png
        小白和小黑:@angelen 在Storyboard里面一样可以设置他的继承呀。

      本文标题:阅读Swifter - 100 个 Swift 必备 tips(

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