特色
(1)优于OC,快速,安全
(2)取消了预编译指令包括宏定义(OC用的太多了)
(3)取消了OC指针和不安全访问的使用(看不到星星了)
(4)舍弃 Objective-C 早期应用 Smalltalk 的语法,全面改成点语法
(5)3.0中对Foundation框架作了不少改变,去除了NS,将绝大部分class转换成struct结构体(为了考虑性能和安全性,绝大部分使用结构体来代替之前的类,可是在实际使用感受不到)
(6)可使用现有的 Cocoa 和 Cocoa Touch 框架
, 之前是OC调UIKit,如今就是Swift调UIKit,这点事没问题的
(7)Swift由于语法的严谨性可让不少错误提早遇到,这样不多出现bug让程序停在main致使没法找到
(8)@UIApplicationMain是程序的入口
(9)只有.h没有.m
(10)全部的代码都包括在{}里,默认方法func都有缩进!
(11)语法的allocinit替换成()java
PlayGround
(1)能够看苹果官方自带的tips和100个tips,都在Placground来使用ios
基础点
(1)不适用self. 在闭包或者编译器提示的时候再使用
(2)分号是用来分割语句的,若是一行洗不少,就能够加分号,通常时候能够不加
(3)#function打印执行的函数
(4)添加标记用到// MARK: - 选择,若是是接下来要作的能够用// TODO:和// FIXME:这些很是有用程序员
var 定义变量,赋值以后仍然能够修改
常量和变量的细节json
// 定义常量而且直接设置数值 let x: Int = 10 // 常量数值一经设置,不能修改,如下代码会报错 // x = 30 let y: Int // 常量有一次设置的机会,如下代码没有问题,由于 `y` 尚未被设置数值 y = 10 // 一旦设置以后,则不能再次修改,如下代码会报错,由于 `y` 已经被设置了数值 // y = 50 print(x + y) // 变量设置数值以后,能够继续修改数值 var z: Int z = 100 z = 200 print(x + y + z)
Swift 可以根据右边的代码,推导出变量的准确类型
只有相同类型的值才可以进行运算swift
// 整数默认的类型是 Int let intValue = 200 // 小数的默认类型是 Double let doubleValue = 10.5 // 若是要对不一样类型的数据进行计算,必需要显式的转换 print(intValue + Int(doubleValue)) print(Double(intValue) + doubleValue)
注意:Swift对类型要求异常严格,任何不一样类型的数据不能直接运算(哪怕是Int和Double),不会作一些自动的转换来转换成Double。Swift不存在基本数据类型,Int和Double都是结构体其实,强转用Double(x)完成,或者在定义的时候直接指定变量的类型let x : Double = 10;(不多使用)api
let num = 100 if num > 10 { print("大 \(num)") } else { print("小 \(num)") }
num > 10 ? print("大 \(num)") : print("小 \(num)")
或者数组
num > 10 ? print("大 \(num)") : () 这样就对后面的不做处理。 () 表示空执行。
let scoreString = "优" switch scoreString { case "优": let name = "学生" print(name + "80~100分") case "良", "中": print("60~80分") case "差": print("不及格") default: break }
switch 的条件判断安全
let score = 90 switch score { case _ where score >= 80: print("优") case _ where score >= 70: print("良") case _ where score >= 60: print("中") default: print("差") }
for i in 0...5 { } for i in 0..<5 { }
for i in (0..<10).reversed() { }
(1)定义变量时,若是是可选的,表示能够有值,也能够是nil,用“?”
(2)强行解包 “!”,程序员来注意!,而且要少用,可能会崩
(3)最多见的错误:解包的时候发现nil。fatal error: unexpectedly found nil while unwrapping an Optional value
(4)let可选的话,没有默认值,须要赋值。var可选的话,默认值为nil
(5)可选项在参与计算时候必须解包闭包
// 格式1 let x: Optional = 20 // 格式2 let y: Optional<Int> = 30 // 格式3 let z: Int? = 10 print(x) print(y) print(z)
var x1: Int? print(x1) let x2: Int? // 常量可选项没有默认值,在赋值以前不能使用 // print(x2) x2 = 100 print(x2)
print(x! + y! + z!)
程序员要对每个 ! 负责app
var optionValue: Int? print(optionValue?.description) // 输出 nil optionValue = 10 print(optionValue?.description) // 输出 Optional("10")
与强行解包对比,可选解包更安全,可是只能用于函数调用,而不能用于计算
因为可选项的值可能为 nil,不容许直接参与运算,所以在实际开发中,常常须要判断可选项是否有值。
若是单纯使用 if,会让代码嵌套层次很深,不宜阅读和维护,为了解决这一问题,苹果提供了如下三种方式:
注意:?? 的优先级低,在使用时,应该注意使用 ()
let x: Int? = 10 let y: Int? = 100 print((x ?? 0) + (y ?? 0))
let name: String? = "Mike" let age: Int? = 18 if let name = name, let age = age { print("\(name) 今年 \(age) 岁") } else { print("姓名或者年龄为 nil") }
func demo(name: String?, age: Int?) { guard let name = name, let age = age else { print("姓名或者年龄为 nil") return } print("\(name) 今年 \(age) 岁") } demo(name: name, age: age)
由于总会取名字,if let name = name这样就能够,注意后面使用的时候用非空的那个!而且iflet和guardlet能够依次判断,先判断是一个字典,再拿字典的数组,在判断数组的值,能够一条线判断出来。
用String,是一个结构体,具备绝大多数NSString功能,支持直接遍历
(1)遍历:
func demo3() { // 字符串遍历(NSString不支持这么遍历) let str = "wowosnshi是" for s in str.characters { print(s) } }
(2)长度:
// 返回指定编码对应的字节数,每一个汉字三个字节 print(str.lengthOfBytes(using: .utf8)) // 返回真正字符串长度 print(str.characters.count)
(3)拼接:要注意可选项拼接不解决会带上Optional,剩下的均可以拼接,不再用看StringWithFormat了
let name = "AA" let age = 19 let title : String? = "sss" print("\(name)\(age)\(title ?? "")")
(4)格式化:
let h = 8 , m = 10, s = 44 // OC中用stringWithFormat格式化日期,Swift中能够 let strDate = String(format: "%02d-%02d-%02d", h,m,s) print(strDate)
(5)截取字符串:建议用NSStrin做中转,由于swift取串方法一直在改变
let str = "红红火火恍恍惚惚" let strOC = str as NSString strOC .substring(to: 1) strOC.substring(with: NSMakeRange(0, 2))
(1)就是中括号,注意数组的类型,而且基本数据类型不须要包装,能够直接方数组里,若是类型不同(混合数组,可是基本不用),自动推导[NSObject]。在Swift中还有一个[AnyObject类型],标示任意对象,由于在Swift中一个类能够没有任何父类。
(2)遍历:
// 遍历1(按照下标遍历) for i in 0..<array.count { } // 遍历2(遍历元素) for s in array { } // 遍历3(同时遍历下标和元素) for e in array.enumerated() { // let e: (offset: Int, element: String) e是一个元组 print("\(e.offset), \(e.element)") } // 遍历4(同时遍历下标和元素) for (n,s) in array.enumerated() { print("\(n),\(s)") } // 反序遍历 for s in array.reversed() { } // 反序索引下标(这样写才对,先枚举再反序) for (n,s) in array.enumerated().reversed() { }
(3)增删改:
array.append("AA") array[1] = "BBB" array.remove(at: 2)
(4)合并:用“+”号。可是要合并的数组的两个类型必须一致。
通常是[String:NSObject],对应键值对.因为3.0后大部分都是结构体了,AnyObject很差用了,Any范围更大
(1)字典数组:
(2)增删改:和数组都相似,就是两个字典合并不像数组直接相加,而是须要遍历
(1)外部参数,当外部参数用_替代的时候,会在外部调用的时候忽略形参名
(2)函数的默认值(OC不具有),这个使Swift比OC灵活不少不少,一个方法能够作不少事,由于OC会有各类参数和组合,Swift只需写一个最多的参数,而后不须要的设定默认值就是了
(3)无返回值 :直接省略 () Void均可以
(4)闭包:相似Block,比Block还普遍。OC中Block是匿名函数,Swift中函数是特殊的闭包。闭包在整个开发中和Block的应用场景同样。用于控制器/自定义视图/异步执行完成的回调。这些回调的特色就是都是以参数回调处理结果,返回值为Void。
let biBao = { (x: Int) -> Int in return x + 100 } print(biBao(10))
func loadData(compeletion:@escaping ( _ result: [String])->()) -> Void { DispatchQueue.global().async { print("耗时操做会得到一些结果 \(Thread.current)") Thread.sleep(forTimeInterval: 1.0) let json = ["天气","不错","刮大风"] // 主线程回调 DispatchQueue.main.async(execute: { print("主线程更新UI \(Thread.current)") // 回调 -> 经过参数传递 执行闭包 compeletion(json) }) } }
调用:
// 执行的适合我就拿到了值 loadData { (result) in print("获取的新闻数据 \(result)") }
尾随闭包:若是函数的最后一个参数是闭包,那么参数就省略了,最后一个参数直接{}大括号包装
闭包的循环引用:
(5)面向对象(各类构造函数):()就是allocInit,在Swift中对应init()。在swift中一个项目全部类都是共享的,能够直接访问,每个类都默认有一个命名空间。A.name B.name God.name Dog.name。同一个类能够从属于不一样的命名空间(假若有一个框架有Person类,作用户,还有一个框架作后台,也用Person。在OC中就只能靠前缀解决,HouTaiPerson,KuangJiaPerson。而Swift中的命名空间就是项目名。AAA项目有一个Person,那么AAA.Person就是AAA的Person类,此时再导入框架,那也是框架的.Person)
思路:OC是先调用爸爸。就是Person,Person会再调用NSObject,就是先跑上去什么都无论,先初始化了NSObject,而后才往下走挨个初始化。Swift是把本身彻底初始化,再上去初始化爸爸,这么看比OC快了一圈,性能要好。
注意:若是重载了构造函数而且没有实现父类的init,那么系统再也不提供init构造函数了(默认是有的),由于默认的构造函数不能给本类的属性分配空间(你不本身写name = ,系统就没办法分配)
KVC构造函数:只需记住下面4点
因此通常在模型中加个? 而后用KVC实现(先调用init由于是运行时机制)
总体
(6)便利构造函数:关键字Convenience(开发用的不多,由于模型都是框架转,UI不须要便利)
*目的:条件判断,只有知足条件才实例化对象,防止没必要要的内存开销,简化对象建立。自己是不负责属性的建立和初始化的。
(7)deinit:相似OC的Dealloc
便利构造函数 + 分类能够省略抽取不少代码。例如给UITextField/UIButton写分类,而后写便利构造函数,方便。
在OC开发中,懒加载通常自定义控件。在Swift中,懒加载仍是须要用的,能够保证控件延迟建立,还能避免处理控件解包。若是直接定义控件var label = UILabel,根据代码从上到下,会让控件在ViewDidLad以前就提早建立了。因此须要懒加载。OC中懒加载就是Get方法,Swift直接lazy var。固然也能够private lazy var来限定做用域。
(1)简单的懒加载:
(2)完整的懒加载:()就是函数执行,就是一个特殊的闭包,因此懒加载本质是一个闭包。通常不这么写。
(3)OC和Swift区别
- (UILabel *)label{ //若是label等于nil就会建立! if (_label == nil) { _label = [[UILabel alloc]init]; _label.text = @"loirou"; [_label sizeToFit]; _label.center = self.view.center; } return _label; }
OC是等于nil时候就懒加载
[self.view addSubview:self.label]; //释放label _label = nil; //会再次调用懒加载的代码 NSLog(@"%@",self.label);
当label设nil的时候就在此调用。在ios6中,didReceiveMemoryWarning是不清理视图的。
此时释放的时候就会报错。由于定义的时候没有?,就是必定有值得。
那么若是定义时候加? 一旦label = nil,也不会在执行懒加载了!由于懒加载根本没写若是是空就建立。
懒加载只会在第一次调用的时候执行闭包。Swift中必定注意不要主动清理视图或控件,由于懒加载不会建立了(例如内存警告别干掉控件,干掉了在也用不成了,由于懒加载就一次)
(1)getter/setter(开发不用):
// 开发通常不用,还给搞一个_name。 // swift通常不会重写getter和setter private var _name: String? // 伪装的一个值 var name: String? { get{return _name} set{_name = newValue}} // get返回成员变量 set记录成员变量 override func viewDidLoad() { super.viewDidLoad() demo() } }
(2)计算型属性:readOnly只读:OC中重写get。Swift也是重写get,无论set就能够。
// 只读,此时set赋值时候就会报错 var name: String? { get{return "ABC"}} // 还有一种简写: var name: String? { return "ABC"}
看这类属性,是自己不保存内容的,都是经过计算得到结果。就能够当我就是个没有参数只有返回值的函数!!我每次return值给你个人任务就完成了。每次你调用我这个属性的时候,我都会进行一次计算!都会执行个人代码而后return给你。我自身不存储的。
(3)懒加载和计算型属性的区别:
(4)存储型属性:须要开辟空间,存储数据。通常的属性都是存储型属性(懒加载)
(5)存储和计算均可以?或者不加。看状况是否是必选
(四)Swift中设置模型数据:
Swift作好模型后。别的控件拿到模型后,由视图本身来显示。此时在didSet里写。就是替代OC的Setter方法。(OC的Setter要考虑_成员变量 = 值,并且若是是copy须要.copy,而Swift不须要考虑一切)
赋值
// 获取命名空间的值,可选 let str = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "" let con = NSClassFromString(str + "." + "ViewController") as? UIViewController.Type
对于任何类均可以知道类的全部属性和方法,对于任何对象均可以调用任何属性和方法,这种动态获取的信息和动态调用对象属性方法的功能成java的反射机制(Swift也有了)
(1)在OC中利用反射机制
(2)在Swift中利用反射机制相似。工做中用的不少不少。
场景:AppDelegate(OC中也用过,利用NSClassFromString得到类,而后设置根控制器。可是Swift中多了一个命名空间写法。)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) // 依据String名字拿到控制器(添加项目名称,命名空间,不能有数字和特殊符号) // 返回的是AnyClass? 须要as?强转 // 控制器添加Type类型 let rootControl = NSClassFromString("SwiftyDemo.ViewController") as? UIViewController.Type let vc = rootControl?.init() window?.rootViewController = vc window?.makeKeyAndVisible() return true }
(3)第三方框架,用了不少反射机制和工厂方法,为了实现大量的解耦和封装,很麻烦。一个方法可能跳10个方法10个控制器才写了一个加法。可是若是涉及高级开发和封装,必需要通过这一步。