swift特性一

    转载请注明出处:http://blog.csdn.net/olo721727175/article/details/30058383


     2014年6月3号早上一起来,就打开手机看看wwdc14发布了什么新东东。之前官方就透露这次发布会给开发者带来好东西。看来这个东西主要就是swift语言了。

    作为来自java的ios开发者,首先要吐槽的就是objc的命名空间了。没有命名空间!只能通过类名前缀来划分业务。如果使用短前缀代码可读性低,如果用长前缀就会让类名过长。然后就是objc的异常处理,最典型的是访问一个对象不存在的方法,会造成crash。用objc提供的异常处理根本catch不到异常,一但这种情况发生就等于给app判了死刑。在内存管理方面,倒是感觉性能上比java好。手机端开发objc使用的是arc或者的mrc,而java使用的是垃圾回收。用过java的人都知道它比较吃内存。部署个java项目到BAE,如果不把内存扩展成1G,那就玩儿不起来。真的希望swift能解决这些问题。

更强
它保留了objc的参数名称(有必要么?)。新曾了很屌的类型联想。新增了命名空间(根本不是这么回事儿)。还提供了闭包、元组、泛型、简约的迭代、强大的结构体—有方法有拓展有协议。

简化了nil
swift的nil可以用于所有数据类型,包括Int,Double,String,对象,数据结构,枚举。而不是想objc那样,对象用nil,数据结构有时用NSNotFound,指针用NULL。

var a:Int? = nil

类型联想

swift不需要我们声明变量的类型,swift会根据变量的值联想到它的类型。

var a:Int = 1
let b:Int = 2
a = 3
// error b = 3

关键词var表示这个变量是可变的,let表示这个变量是不可变的。swift是强类型语言,目的是把错误推到编译时发生,减少运行时bug出现的几率。这里数据类型是通过变量声明时的值类型来决定的。但是如下面这样又该如何处理呢?

// Double
var a = 1.0

这里变量a,根据1.0被联想成了Double值(默认联想)。我们如果想要Float值改怎样处理呢?

// a被显式定义成了Float
var a:Float = 1.0

这里有个编程习惯的问题,如果确定这个变量在未来的使用中不会被改变,那就把它声明为let的。这样在团队开发中,会告诉你的小伙伴这个值是不可变了,不要去修改它。增加了代码的可读性。也避免了在未来被程序意外修改。

选项(optional)

optional将变量分成两类,一类是变量的值有可能是nil的。另一类是变量一定不会是nil的。
optional有两个符号—“?”和“!”,“?”表示这个值被包装成optional了,表示这个值可以是nil,也可以是具体的类型值。!表示这个值一定不会为nil。

var i:Int? = nil
// error
// println(i!);
println(i);

这个“!”表示解包,这是在开发者确定这个值不是nil的时候使用。如果这个值是nil,那就会出现运行时错误。如果不使用“!”直接打印,如果值是nil就会直接打印nil不会出现错误。


上图是在命令行(swift -repl,以后会提到),得到的错误。通过错误可以看到,optional的nill实际上是Optional.None。而普通的值是Optional.Some,为什么这么说?

enum Optional<T> : LogicValue, Reflectable {
    case None
    case Some(T)
    init()
    init(_ some: T)

    /// Allow use in a Boolean context.
    func getLogicValue() -> Bool

    /// Haskell's fmap, which was mis-named
    func map<U>(f: (T) -> U) -> U?
    func getMirror() -> Mirror
}

上面是swift自带的枚举声明。通过声明可以知道,nil是Optional.None,具体类型值是Optional.Some。这就是为什么官方文档经常说:“forced upwrapping”—解包。解包有关发方法大概就是上面代码中的最后一个方法了吧,这点需要进一步验证。当然apple发布runtime相关文档后,这些估计就迎刃而解了。

像上面提到的那样,通过“!”放在变量后边表示解包,如果这个变量的值是nil,就会发生运行时错误。
我们可以通过下面的方式判断一个变量是否为nil。

var a:Int? = 123

if a {
    a
}

我们也可以在判断的时候赋值,官方说是“optional 绑定”,经过实验,非optional类型是不能用这种形式的。

var a:Int? = 123
if let b = a {
    b
}

在swift中,非optional变量是不会被自动初始化的。

         var a:Int
        // error
//        println(a);
        var b:Int?
        println(b) // print nil
        var c:Int!
        println(c) // print nil

发现,这块的东西已经有人说了,这里就不再多赘述了,请移步:http://joeyio.com/ios/2014/06/04/swift---/

这里小结下,“?”的鲁棒性高些,它在变量后边表示“如果变量为nil,请返回nil”。“!”在使用的时候要消息,确定这个值一定不是nil,或者这个方法一定被实现了再使用。

数据类型

swift的基本数据类型包括Int,Double,String等。这些就不提了。这里主要说说Dictionary、Array。他们和我们在objc中接触的NSDictionary,NSArray不同。他们不是对象,而是机构体。也就是在赋值的时候,copy的不是指针或者引用,而是结构体。这样变量存储的就不是指针或者引用而是结构体了。每一次赋值,或者每一次方法参数传递都会参数结构体的copy,方法内部对结构体提的修改都不会影响结构体本身。但实时上,Dictionary是符合上面规律的,Array要特殊一些,他的结构体copy时机并不在复赋值或者参数传递上,而是在Array长度改变上。

这里我们要再理解下var以及let关键字,var关键字表示“我的值是可以变的”,let表示“我的值是不可以变的”。那么我们在修改let的Dictionary的key对应的值的时候,内容有没有变呢?我们修改了key对应的value的时候,结构体内容当然变了,所以let Dictionary是不允许修改key对应的值的。

let dic = ["a":1,"b":2]
// error
// dic["a"] = 3

普通的结构体在赋值和参数传递的时候就会引起copy,包括Dictionary。

var map = ["a":1,"b":2]
var map1 = map
map["a"] = 3

// map和map1的值不同
map
map1

但是Array有些特别,它是在长度改变的时候引起copy,比如删除几个item,长度变短了。或者添加一个item导致长度变长了。

var a = [1, 2, 3]
var b = a
b += 100
var c = a

a[0] = 100

a // print [100,2,3]
b // print [1,2,3,100]
c // print [100,2,3]

上面的例子中,b添加了一个item 100,导致它的长度变长了。数组b被copy了一份,a和c还是共用一份。这里的“+=”相当于b.append(100)。
如果我们想要显示的让数组bcopy,还可以调用它的unShare方法。

var a = [1, 2, 3]
var b = a
b.unshare()
var c = a

a[0] = 100

a // print [100,2,3]
b // print [1,2,3]
c // print [100,2,3]

swift提供了符号”===“判断两个array是否使用的是同一个空间。为什么“==”不能?“==”判断是是结构体的数据是否相同,如:

var a = [1, 2, 3]
var b = a
b.unshare()
if a == b {
    a // 只是说明a和b的内存中的内容相同。内存块不一定是同一个
}

这里b使用了unshare方法,a和b使用的不是同一个空间区域。但是a==b确实true。原因就是a和b虽然内存位置不同,但是他们的内容相同。swift为我们提供了判断两个变量是否使用同一个内存空间的操作符号“===”,这个方法同样使用结构体和对象。
我们同样可以通过copy方法达到copy的效果。但是它的效率要比unshare方法低,copy方法一但被调用就会马上被执行。而unshare方法会将copy动作推迟到真正需要的时候。而copy的意思更直观一些,我们也比较熟悉。

var a = [1, 2, 3]
var b = a.copy()

if a===b {
    "b和a使用的是同一块内存区域"
}else{
    "b和a使用的不是同一块内存区域"
}

这里简单写上字符串内容的原因是这段代码是在storyground上运行的。

这里拓展以下,Int、Float、Dictionary,Array等都是结构体,而结构体和枚举都是值类型。他们的特点就是赋值代表copy。“==”代表比较内容。他们和对象比有什么缺点和有点呢?最直接的缺点就是值类型在赋值的过程中会多copy一份,浪费空间。但是由于多个变量不是指向同一个内存空间,所以安全性要高些。

结构体

结构体和类几乎一样,都可以定义属性,定义方法,实现协议,初始化方法,定义拓展,定义下标支持。但是类又多了继承,运行时类型检查以及转换,deinit方法允许释放资源,引用计数。总得来说,类多了内存管理和继承。结构体的设计初衷就是为了简单封装一些数据,并不需要太多的面向对象的特性,比如继承。而且我们看到使用结构体的大多是一些基本数据类型的简单封装,比如CGRect、CGSize。

struct Resolution {
    var width = 0
    var height = 0
}

class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

var resolution = Resolution(width:20,height:22)

// error
//let videoMode = VideoMode(resolution:nil,interlaced:false,frameRate:111,name:"good")

这里要再提以下,如果resolution是被声明为let的,resolution的属性是不能被修改的,因为let表示变量保持的这块内存空间不可变。

属性在对象初始化前都会被赋值,但是如果一个属性占用的空间非常大,而这个属性又暂时不需要,那就会白白浪费空间。所以swift提供了@lazy标记,让属性只有在真正使用的时候才会被创建。

struct Resolution {
    var width:Double
    let height:Double
    init(_ width:Double,_ height:Double){
        self.width = width
        self.height = height
    }
}

class LazyClass{
    // 最SB的魔数命名
    @lazy var r1 = Resolution(1,2)
    var r2 = Resolution(3,4)
}

// 在playground中:{{width 3.0,height:4.0},nil}
var lazy = LazyClass()
lazy.r1

从例子中可以看到,r1是nil,r2被创建了。

我们可以定义作用域在类(class)级别,或者类型(结构体,枚举)级别的属性。

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 0
    }
}
enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 0
    }
}
class SomeClass {
    class var computedTypeProperty: Int {
        return 0
    }
}


未完待续...




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值