美文网首页
Swift基础13

Swift基础13

作者: cht005288 | 来源:发表于2016-05-23 11:37 被阅读0次

    高级运算符

    与c语言中的算术运算符不同,Swift中的算术运算符默认是不会溢出的。所以溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用Swift中另一套默认溢出的运算符,比如溢出加法运算符(&+)。所有的这些溢出运算符都是以 & 开头的。

    在自定义结构体、类和枚举时,最好也同时为它们提供标准Swift运算符的实现。Swift简化了运算符的自定义实现,也使判断不同类型所对应的行为更为简单。

    我们不用被预定义的运算符所限制。在swift当中可以自由地定义中缀、前缀、后缀和赋值运算符,以及相应地优先级与结合性。这些运算符在代码中可以像预设的运算符一样使用,我们甚至可以扩展已有的类型以支持自定义的运算符。

    位运算符

    位运算符 可以操作一个数据结构中每个独立的位。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。

    Swift支持c语言中的全部位运算符,具体如下:

    • 按位取反运算符 (~)可以对一个数值的全部位进行取反:按位取反操作是一个前置运算符,需要直接放在操作数的之前,并且它们之间不能添加任何空格。
    let inintialBits : UInt8 = 0b00001111
    let invertedBits = ~initialsBits //等于0b11110000
    

    按位与运算符

    按位与运算符 (&)可以对两个数的比特位进行合并。它返回一个新的数,只有当两个操作数的对应位都为1的时候,该数的对应位才为 1.

    let first: UInt8 = 0b11110000
    let second: UInt8 = 0b00011111
    let thrid = first & second //0b00010000
    
    

    按位或运算符

    按位或运算符(|)可以对两个数的比特位进行比较,它返回一个新的数,只有两个操作数的对应位中有任意一个为1时,该数的对应位就是1.

    按位异或运算符

    按位异或运算符 (^)可以对两个数的比特位进行比较。它返回一个新的数,当两个操作数的对应位不相同时,该数的对应位就为 1.

    let firstBits: UInt8 = 0b00010100
    let otherBits: UInt8 = 0b00000101
    let outputBits = firstBits ^ otherBits // 等于 00010001
    
    

    按位左移/右移 运算符

    按位左移运算符 (<<)和按位右移运算符 (>>)可以对一个数进行指定位数的左移和右移,但是需要遵守下面定义的规则。

    对一个数进行按位左移或按位右移,相当于对这个数进行乘以2 或除以2的运算。将一个整数左移一位,等价于对这个数乘以 2 ,同样地,将一个整数右移一位,等价于将这个数除以 2 。

    1、无符号整型进行移位操作
    对无符号的整型进行移位的规则如下:

    • 已经存在的比特位按指定的位数进行左移和右移。
    • 任何移动超出整型存储边界的位都会被丢弃。
    • 用0来填充移动后产生的空白位。

    2、有符号整型的移位操作
    对比无符号整型来说,有符号整型的移位操作相对复杂的多,这种复杂性源于有符号整型的二进制形式。

    有符号整数使用第1个比特位(通常被称为符号位)来表示这个数的正负。符号为0 代表正数,为1代表负数。

    其余的比特位(通常被称为数值位)存储了这个数的真实值。有符号正整数和无符号数的存储方式是一样的。

    0 0000 100 = 4
    符号位为0,说明这是一个正数,另外7位则代表了十进制数值 4 的二进制表示。

    负数的存储方式略有不同。它存储的是 2 的n次方减去它的真实值绝对值,这里的n为数值位数。一个8位的数有7个数值位,所以是 2 的7次方,即128.

    1 1111100 = -4

    这次的符号位为 1 ,说明这是一个负数,另外 7 位则代表了数值 124 即(124 - 4)的二进制表示。

    负数的表示通常被称为二进制补码表示法。

    使用二进制补码可以使负数的按位左移和右移操作得到跟正数同样的效果,即每左移一位就将自身的数值乘以2,每向右一位就将自身的数值除以2 。 要达到此目的,对有符号整数的右移有一个额外的规则:

    • 当对整数进行按位右移操作时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 0 。

    这个行为可以确保有符号整数的符号位不会因为右移操作而改变,这通常被称为算术移位。

    溢出运算符

    在默认情况下,当向一个整数赋值超过它容量的值时,Swift默认会报错,而不是生成一个无效的数。这个行为给我们操作过大或着过小的数的时候提供了额外的安全性。

    然而,也可以选择让系统的数值溢出的时候采取截断操作,而非报错。可以使用Swift提供的三个溢出操作符来让系统支持整数溢出运算。这些操作都是以&开头的:

    • 溢出加法 &+
    • 溢出减法 &-
    • 溢出乘法 &*

    数值溢出

    数值有可能出现上溢 或者 下溢 。

    var unsignedValue = UInt8.max
    print("\(unsignedValue)") //255
    
    
    unsignedValue = unsignedValue &+ 1
    print("\(unsignedValue)") //0
    
    var value2 = UInt8.min
    value2 = value2 &- 1
    

    溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位爷粗腰参与计算。

    var value2 = Int8.min
    value2 = value2 &- 1
    

    总结:对于无符号与有符号整型数值来说,当出行上溢时,它们会从数值所能容纳的最大数变为最小的数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大的数。

    优先级和结合性

    运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。

    结合性 定义了相同优先级的运算符是如何结合的。

    运算符函数

    类和结构体可以为现有的操作符提供自定义的实现,这通常被称为运算符重载。

    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)
    }
    
    let vector = Vector2D(x: 3, y: 1)
    let vector2 = Vector2D(x: 2.0, y: 4.0)
    let combinedVector = vector + vector2
    

    前缀和后缀运算符

    类与结构体也能提供标准单目运算符的实现。单目运算符只有一个操作目标。当运算符出现在操作目标之前时,它就是前缀(prefix)的(比如, -a),而当它出现在操作目标之后时,它就是后缀(postfix)的(比如,i++)

    要实现前缀或者后缀运算符,需要在声明运算符函数的时候在func关键字之前指定prefix或者postfix 限定符:

    prefix func - (vecotr : Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
    
    

    符合赋值运算符

    符合赋值运算符将赋值运算符(=)与其他运算符进行结合。比如,将加法与赋值结合成加法运算符(+=) 。在实际的时候,需要把运算符的左参数设置为 inout类型,因为这个参数的值会在运行符函数本身被修改。

    func += (inout left: Vector2D, right: Vector2D) {
         left = left + right
    }
    
    

    note:不能对默认的赋值运算符(=)进行重载。只有组合赋值符可以被重载。同样地,也无法对三目条件运算符 a ? b : c 进行重载。

    等价操作符

    自定义的类和结构体没有对等价操作符 进行默认实现,等价操作符通常被称为 “相等” 操作符(==)与 “不等” 操作符 (!=) 。 对于自定义类型,Swift无法判断其是否“相等”,因为“相等”的含义取决与这些自定义类型在你的代码中扮演的角色。

    func == (left: Vector2D, right: Vector2D) -> Bool {
         return (left.x == right.x) && (left.y == right.y)
     }
     func != (left: Vector2D, right: Vector2D) -> Bool {
         return !(left == right)
     }
    
    

    自定义运算符

    除了实现标准运算符,在Swift当中还可以声明和实现自定义运算符。

    新的运算符要在全局作用域内,使用 operator关键字进行声明,同时还要指定prefix、infix或者postfix限定符:

    
    struct Vector2D {
       var  x = 0.0, y = 0.0
    }
    
    prefix operator +++ {
    
    }
    
    
    prefix func +++ (inout vector: Vector2D) ->Vector2D {
       vector.x += 1
       vector.y += 1
       return vector
    }
    
    var begin = Vector2D(x: 1.0, y: 4.0)
    +++begin
    
    print("\(begin)")
    
    

    自定义中缀运算符的优先级和结合性

    自定义的中缀(infix)运算符也可以指定优先级和结合性。

    结合性 可取的值有 left 、right 和 none 。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的操作数进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的操作符进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。

    结合性默认值时none ,优先级如果没有指定则默认为 100.

    struct Vector2D {
        var  x = 0.0, y = 0.0
    }
    
    infix  operator +- {
    associativity left precedence 140
    }
    
    
    func +- (left: Vector2D,right:Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y - right.y)
    }
    
    let value1 = Vector2D(x: 1.0, y: 2.0)
    let value2 = Vector2D(x: 3.0, y: 4.0)
    let value = value1 +- value2
    
    print(value)
    
    

    note:当定义前缀与后缀操作符的时候,我们并没有指定优先级。然而,如果对同一个操作数同时使用前缀与后缀操作符,则后缀操作符会先被执行。(前缀和后缀操作符不能指定结合性和优先级)

    Paste_Image.png

    相关文章

      网友评论

          本文标题:Swift基础13

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