Swift和OC的区别(基于Swift3.0)
swift无疑是IOS程序员的未来,但是目前苹果公司还在不端更新和改变swift,甚至在语法层面还在不断探索,虽然xcode提供一些直接适配新版本swift语法的快捷键,但给我们学习不免造成一些影响,这里是基于swift3.0和oc的一些基础性知识的比较。
一.基础部分
1.Swift的类型是在C和Objective-C的基础上提出的,Int是整型;Double和Float是浮点型;Bool是布尔型;String是字符串。Swift还有两个有用的集合类型,Array和Dictionary,(类型第一个字母大写)
2.Swift还增加了Objective-C中没有的类型比如元组(Tuple)。元组可以让你创建或者传递一组数据,比如作为函数的返回值时,你可以用一个元组可以返回多个值。
元组(tuples)把多个值组合成一个复合值。元组内的值可以使任意类型,并不要求是相同类型。
let http404Error =(404,"Not Found")
(404,"Not Found")元组把一个Int值和一个String值组合起来表示HTTP状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为(Int,String)的元组”。
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为(Int,Int,Int)或者(String,Bool)或者其他任何你想要的组合的元组。
你还可以通过下标来访问元组中的单个元素,下标从零开始:
println("The status code is \(http404Error.0)")
你可以在定义元组的时候给单个元素命名:
let http200Status =(statusCode: 200,description: "OK")
给元组中的元素命名后,你可以通过名字来获取这些元素的值:
println("The status code is \(http200Status.statusCode)")
注意:元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。
3.Swift还增加了可选(Optional)类型,用于处理值缺失的情况。可选表示“那儿有一个值,并且它等于x”或者“那儿没有值”。可选有点像在Objective-C中使用nil,但是它可以用在任何类型上,不仅仅是类。可选类型比Objective-C中的nil指针更加安全也更具表现力,它是Swift许多强大特性的重要组成部分。
Swift定义的常量和变量是没有默认值的,所以引入了可选的概念,用?修饰变量,标示该变量的值可能为空也可能为某个值,然后获取的时候用!强制解析,此时必须有值,否则报运行时错误。
比如你定义一个String类型的变量
var str:String?
str="ass"
print("str=\(str!)")
如果不使用!强制解析,可以使用可选绑定(optional binding)来判断可选是否包含值,如果包含就把值赋给一个临时常量或者变量。
if let realstr = str
{
print("realstr=\(realstr)")
}else
{
print("str为空")
}
有的函数的返回值就是可选类型,默认是使用?修饰的,也需要!强制解析或者可选绑定解析。
let convertedNumber = possibleNumber.toInt()
// convertedNumber被推测为类型"Int?",或者类型"optional Int"
ifconvertedNumber {
println("\(convertedNumber!)")
}else{
println("could not be converted")
}
隐式解析可选
有时候在程序架构中,第一次被赋值之后,可以确定一个可选总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
这种类型的可选被定义为隐式解析可选(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选。
let possibleString: String?= "An optional string."
println(possibleString!)//需要惊叹号来获取值
//输出"An optional string."
let assumedString: String! = "An implicitly unwrapped optional string."
println(assumedString)//不需要感叹号
//输出"An implicitly unwrapped optional string."
自判断链接
自判断链接(Optional Chaining)是一种可以请求和调用属性、方法及子脚本的过程,它的自判断性体现于请求或调用的目标当前可能为空(nil)。如果自判断的目标有值,那么调用就会成功;相反,如果选择的目标为空(nil),则这种调用将返回空(nil)。多次请求或调用可以被链接在一起形成一个链,如果任何一个节点为空(nil)将导致整个链失效。
如果用!解析一个可选类型为空时会报运行时异常
如果改用?解析就可以避免
print(stu.room!.person.name)
room==nil所以报运行时错误
print(stu.room?.person.name)
就打印nil
更正规的写法可以使用if let
如果numberOfRooms为Int类型,john.residence?.numberOfRooms就是Int?类型
iflet roomCount = john.residence?.numberOfRooms
{
}else
{
}
如果printNumberOfRooms()返回值为Void类型,john.residence?.printNumberOfRooms()就是Void?类型
ifjohn.residence?.printNumberOfRooms()
{
}else
{
}
4.常量的值一旦设定就不能改变,而变量的值可以随意更改。常量和变量必须在使用前声明,用let来声明常量,用var来声明变量。所谓改变,对于基本数据类型而言,值改变就是改变,而对于引用数据类型而言,指针指向改变才是改变。
Swift中整数(Integer)、浮点数(floating-point)、布尔值(Booleans)、字符串(string)、数组(array)和字典(dictionaries),都是值类型,并且都是以结构体的形式在后台所实现。
eg: var arr=[“a”,”b”,”c”]
var arrcopy =arr
//此时arr-“a,b,c”arrcopy-“abc”
arrcopy[0]=“fuck”
//此时arr-“a,b,c”arrcopy-“fuck,b,c”
class Person
{
varname:String=“a”
}
var arr=[person1,person2,person3]
var arrcopy =arr
//此时arr,arrcopy指向一块区域,一同改变
5.类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用typealias关键字来定义类型别名。
当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
typealias AudioSample = UInt16
定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:
varmaxAmplitudeFound = AudioSample.min
6.控制流
两种形式的for循环
//传统获取index的语法
for i in 0…10
{
}
//快速访问对象的语法
for obj in arr
{
}
二.函数
普通函数定义和使用
func add(param1:String,param2:String)->String
{
return param1+param2
}
print(add(param1: "w",param2: "q"))
可以给参数添加默认值,使用时也可以不传此参数
func add2(param1:String,param2:String="q")->String
{
return param1+param2
}
print(add2(param1: "w"))
inout修饰函数参数
变量形参只能在函数本身内改变。如果你想让函数改变形参值,并想要在函数调用结束后保持形参值的改变,那你可以把形参定义为in-out形参。
func change(param:inout String)
{
param="wowo"
}
var kaka:String="kaka"
change(param: &kaka)
print(kaka)//此时kaka=“wowo”
复杂函数定义和使用
函数可以作为形参和另一个函数的返回值
三.类
class Person: NSObject {
//普通属性方法定义
var name:String?
func sayHi(mes:String)->String{
return "person\(mes)"
}
//静态属性方法定义(属性用static修饰,方法用static或class修饰)
static var age:String?
class func sayMessage()
{
print("i am person\(age!)")
}
}
属性
lazy修饰的属性必须初始化它会在使用该属性的时候才去执行初始化的代码一般用在消耗不少时间的变量上比如文件打开
lazy特性;局部范围的常量或变量不会延迟计算。
set get方法
//既有set又有get方法
var firstName:String
{
get
{
return "first"+self.name!
}
//newValue代表set参数值
set
{
self.name="setName:"+newValue
}
}
//只有get方法
var lastName:String
{
return "last"+self.name!
}
属性监视器
willSet didSet方法
willSet在设置新的值之前调用
didSet在新的值被设置之后立即调用
//需要设置默认值
var idcard:Double = 0.0
{
willSet
{
print("willSet")
}
didSet
{
print("didSet")
}
}
继承
如果子类实现和父类相同的方法或者属性就是重写。必须用overriding关键字表明。
重写overriding方法
class Student: Person {
override func sayHello(mes: String)-> String {
//可以使用super关键字调用父类方法
return super.sayHello(mes:mes)
}
}
重写overriding属性
override var name: String?
{
set
{
}
get
{
return super.name
}
}
原则:你可以将一个继承来的只读属性重写为一个读写属性,只需要你在重写版本的属性里提供getter和setter即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
防止重写
你可以通过把方法,属性或附属脚本标记为final来防止它们被重写
构造方法
与Objective-C中的构造器不同,Swift的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
构造器分位指定构造器和便利构造器
原则:指定构造器是向上调用的,便利构造器是横向调用的.
规则1:指定构造器必须调用其父类的指定构造器
规则2:便利构造器必须调用同一类中调用的其他构造器
规则3:便利构造器必须以调用一个指定构造器结束
//指定构造器(默认构造方法),因为父类也有该方法,所以用override
override init()
{
super.init()
print("Student init")
}
//指定构造器(自定义构造方法,父类没有该方法,不需要override)
init(size:Double)
{
super.init(size: size)
print("Student init with size")
}
//便利构造器(使用关键词convenience,需要调用自己的构造方法)
convenience init(size:Double,friendNums:Double){
self.init(size:size)
self.friendNums=friendNums
}
类扩展
相当于OC分类
Swift中的扩展可以:
1.添加计算型属性和计算静态属性
注意:扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)
2.定义实例方法和类型方法
3.提供新的构造器
4.定义下标
5.定义和使用新的嵌套类型
6.使一个已有类型符合某个接口
extension Room
{
var newSize:Double
{
return self.size+100
}
func sayExtensionRoom(){
print("sayExtensionRoom\(self.newSize)")
}
}
协议
四.自动引用计数
总体基本和oc一致,在解决循环引用的问题有点区别。
可以使用弱引用weak和无主引用unowned修饰一方变量。
1.两个属性的值都可能是nil,并有可能产生强引用环。这种场景下适合使用弱引用。
var a:A?
weak b:B?
2.一个属性可以是nil,另外一个属性不允许是nil,并有可能产生强引用环。这种场景下适合使用无主引用。
var a:A?
unowned b:B
override init(){
self.b=B()
}
3.两个属性都必须有值,且初始化完成后不能为nil。这种场景下,则要一个类用无主引用属性,另一个类用隐式展开的可选属性。
var a:A!
override init(){
self.a=A()
}
unowned b:B
override init(){
self.b=B()
}
五.类型转换
检查类型
用类型检查操作符(is)来检查一个实例是否属于特定子类型。类型检查操作符返回true若实例属于那个子类型,若不属于返回false。
library中添加Movie和Song
foriteminlibrary {
ifitem is Movie {
++movieCount
}elseifitem is Song {
++songCount
}
}
向下转型(简称下转)
某类型的一个常量或变量可能在幕后实际上属于一个子类。你可以相信,上面就是这种情况。你可以尝试向下转到它的子类型,用类型转换操作符(as)
如果强制转型肯定成功,可以使用as!,如果使用as?,那么返回结果成功则为该转的类型,失败则为空,此时可以使用if let来接收判断
Any和AnyObject的转换
Swift为不确定类型提供了两种特殊类型别名:
-
AnyObject可以代表任何class类型的实例。
-
Any可以表示任何类型(基础类型和class类型),除了方法类型(function types)。
六.闭包
Swift中的闭包与C和Objective-C中的blocks有点类似。
闭包表达式语法
{(parameters)-> returnTypein
statements
}
捕获
闭包可以在其定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
//makeIncrementor函数返回值是一个()->Int类型的函数
func makeIncrementor(amount: Int)->()-> Int {
var runningTotal = 0
func incrementor()-> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
//闭包捕获amount参数的值,然后每次都可以使用
let incrementByTen = makeIncrementor(amount: 10)
print(incrementByTen())//10
print(incrementByTen())//20
print(incrementByTen())//30
七.应用
swift读取文件为String类型
let path:String = Bundle.main.path(forResource: "demo2.js",ofType:nil)!
if let spath = try?String.init(contentsOf: URL.init(fileURLWithPath: path),encoding:String.Encoding.utf8)
{
}else
{
}