编程语言(任何一种)
数据类型(定义+操作) + 流程控制(一般相同)
目录
1. 调试、注释、异常
2. 基本语法
语言特性
类型安全
不能向Double型变量中传入String类型数据、Int型;
不能溢出;
不能隐式转换;
类型推断
根据初始值推断变量类型;
在函数传参过程中字面常量会被自动推导成参数类型;
编译时推断;
1. 不需要分号,若一行多个语句则需要分号;
2. 所有数据类型为结构体或类;
3. 编译器会对数据类型进行越界检查 var a:Int8=128 则报错;
4. 可直接使用其他swift文件中定义的类型;
面向对象
封装
功能在内部实现,只向外界提供接口(松耦合,适合多人开发)
继承
子类可以继承父类的属性和方法(简化代码量)
多态
子类指针不能指向父类实例,父类指针能指向子类实例(简化代码量)
func method(person:Person){ // solder、child、。。。要不然得写子类个数个此方法
}
1. 调试、注释、异常
1.1 调试
断点
普通断点
条件断点
全局断点
打印函数
// 自动换行
print("Hello, World!")
// 不换行 (加 terminator: "")
print("Hello”, terminator: "")
print(i)
print("i=\(i)") \(变量名)中的变量名可以是任何类型
print(nil) 会崩溃
print("\(nil)”) 不会崩溃
print(#file) 打印此文件
print(#line) 打印当前行
print(#column) 打印列数
print(#function) 打印函数
NSLog(str)
NSLog(“%@”,str)
断言
断言(用于调试)
可以查看到非法状态的位置(仅在调试环境中有效?)
用于在运行时检验 对应条件是否为true。(true继续执行,false停止执行)
2种:
assert(条件,”出错提示”) 全局函数 // 出错提示可以忽略
assertionFailure("A person's age can't be less than zero.") //
例
assert(age>0,”年龄必须大于0”) // 当age>0时继续执行,否则输出错误提示
assert(age>0)
先决条件
先决条件在调试环境和生产环境中都可运行
precondition(index > 0, "Index must be greater than zero.")
unchecked模式下,先决条件将不会进行检查。然而使用fatalError(_:file:line:)会进行检查
1.2 注释
1.
// 单行注释
2.
/**/ 多行注释(可嵌套在 // 和 /**/ 中)
3.
条件编译 注释代码块(可嵌套)
#if false
// 代码块1被注释
#else
// 代码 2
#endif
4.高效注释(3种) (在编辑器右上方查看路径【方便定位】)
// MARK: hello hello
// TODO: hello TODO: hello
// FIXME: hello FIXME: hello
类似OC的 #pragma mark hello
[终端传值]
let str=NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding: NSUTF8StringEncoding)
var n=(str?.doubleValue)! // 可能会崩 可选空链
Stirng(str!) as String // 接收字符串,注意小心回车\n 【要删掉最后一个字符\n】
[休眠]
sleep(1) // 休眠1s
[分号]
let x=3 // 分号可以不写
let x=3; var y=4 // 多个语句时必须写分号
[进制转换]
十进制 —> 二进制字符串
String(8,radix:2) 返回类型是字符串:“1000”
String(8,radix:8)
String(8,radix:16)
二进制字符串 —> 十进制
// 二进制字符串 转 十进制
func binary2dec(str:String)->Int{
var a:Double=0
var count:Double=Double(str.characters.count-1)
for c in str.characters{
a+=Double("\(c)")! * pow(2.0, count)
count-=1
}
return Int(a)
}
十六进制字符串 —> 十进制
func changeToInt(var str:String)->Int{
str = str.uppercaseString
str.removeRange(Range(start: str.startIndex,end: str.startIndex.advancedBy(2)))
var sum:Double=0
var count:Double=Double(str.characters.count-1)
for c in str.unicodeScalars{
if c.value>64{
sum+=Double(c.value-55)*pow(16.0,count)
}else{
sum+=Double(c.value-48)*pow(16.0,count)
}
count-=1
}
return Int(sum)
}
print(changeToInt("0xAF"))
1.3 捕获异常
在知道某段代码可能发生异常时,捕捉异常作相应处理。
通常将异常封装成一个枚举,用协议ErrorType约束。
a.使用assert断言时若某条件不满足 程序退出,
b.若函数中throw抛出异常,必须在参数列表之后写 throws
c.在调用能抛出异常的函数时 , 必须用 try 或 try? 或 try! 来标记
// 1.自定义异常类型(遵守Error)
enum MYErrorType:Error{
case OpenFileFail // 打开文件异常
case overFlow // 溢出异常
}
// 2.捕捉异常
do{
// 抛出异常
throw MyErrorType.overFlow
}catch MYErrorType.overFlow{
// 处理该异常
}catch{
// 处理其他异常
}
// 抛异常到调用此方法的地方
func func1()throws{ // 函数中抛异常其未处理时,此处必须加throws 交给上层处理
throw MYErrorType.overFlow
}
do{
func1()
}catch{
}
例:
do{
try audioPlayer=AVAudioPlayer(contentsOfURL: path!)
}catch let error as NSError{
print("创建播放器出错:\(error)")
}
或
try! audioPlayer=AVAudioPlayer(contentsOfURL: path!) // 转可选(危险:转失败则程序崩)
或
try? audioPlayer=AVAudioPlayer(contentsOfURL: path!) // 转可选
let x = try? someThrowingFunction()
let x = try someThrowingFunction()
[指定清理操作]
defer {
...
}
defer语句将代码的执行延迟到当前的作用域退出之前。
defer语句不能包含任何控制转移语句,例如break、return语句,或是抛出一个错误。
defer语句顺序从后往前执行
2. 基本语法
关键字
用于声明:
class、deinit、enum、extension、func、import、init、let、protocol、 static、struct、subscript、typealias、var
用于语句:
break、case、continue、default、do、else、fallthrough、if、in、 for、return、switch、where、while
用于表达和类型:
as、dynamicType、is、new、super、self、Self、Type、__COLUMN__、__FILE__、__FUNCTION__、__LINE__
特定上下文中保留:
associativity、didSet、get、infix、inout、left、mutating、 none、nonmutating、operator、override、postfix、precedence、prefix、right、set、unowned、unowned(safe)、unowned(unsafe)、weak、willSet(在特定上下文之外可以被 用于标识符)
符号(即标识符)
可以作为变量名,函数名,类型名
使用字母、_、数字(所有Unicode字符)组成,且不能以数字开头的字符串。
注意:
不能包含数学符号、箭头、不合法的Unicode编码;
不能为关键字,若要用:`关键字`;
最好不要为中文;
变量和常量
变量
变量使用var声明,值可变
var i:Int=10
var x=0,y=1,z=2
在代码中用标识符(即变量名)表示一个数据,在程序运行时会在内存中创建一个实体(该字符的unicode标量)和标识符映射(内存中的实体即为变量的实体,一个变量名也叫一个实体的引用)
常量
常量使用let声明,值不可变
let max=100
和变量的区别是声明之后值不能再修改
注意:
1.常量不可以改为变量,变量不可以改为常量
2.同一作用域内不可以出现同名的变量或常量
3.用关键词做变量名时 用``包住(尽量不用关键词)
绑定
例 let (x,y) x=元组第一个值 y=元组第二个值
坑:
let定义的对象可被修改:let arr=NSMutableArray()
var定义的对象可以不能修改:var arr=NSArray()
访问范围关键字(范围从大到小)
public 可以在一切地方使用。
internal 默认(可以在整个项目中使用)。
private 只能在本文件内使用。
全局变量、局部变量
全局变量
没有定义在函数、方法、闭包或类型 上下文中的 变量。(全局常量和变量都是延迟计算的,但不需lazy)
局部变量
定义在函数、方法或闭包 上下文中的 变量。
2.1 数据类型
抽象数据类型:
一种数据类型和基于此数据类型可进行的操作
实例化一个对象,即表示:
生成一个该类型的实例
数据类型规则:
所有类型的名称的首字母大写
所有类型遵守Hashable协议(即都有Hash值,如:“a”.hashValue)
强数据类型,不能赋值其他的数据类型。
类型别名(混淆)
typealias MyInt = Int
var x:MyInt=1
基本数据类型(值类型,本质:结构体)
Int(整型)
Float和Double(浮点型)
Bool(布尔型)
String(字符串型)
集合类型(Array和Dictionary、Set)
结构体、枚举
高级类型:元组(一组值)、可选类型(用于处理无值的情况,可以被用于任何类型)
系统自带类类型(引用类型)
自定义类型(引用类型)
类型定义关键字 类型名{
属性1:类型1
…
行为1
…
}
类型注释
var x: Int=3 变量名+冒号+空格+类型名
var red, green, blue: Double
用来声明变量存储值的类型。(编译器会根据初始值自动推断,一般不用)
强制类型转换(实质:创建了一个新的变量或常量)
强制转换后运算符两侧类型必须一致
Double(3)
Int(3.14)
Float(3)
可选类型(可空类型)
当某个变量可能有值,也可能为空(即没有实体映射),则必须定义为可空类型。
写法:
Optional<类型> 或 任何类型? 表示可选
注意:
1.nil只能赋值给可空类型变量,nil在swifit中表示缺失值不是指针。任何类型的可选状态都可以被设置为 nil。
2.可空类型和具体类型(Int和Int?)本质不同,不能做操作。
3.变量名! 取值。强制去包装,表示解析可空变量,为nil时则崩溃;
4.变量名打印:Option(初始值)
5.var x:Int?=10; x==10为true
6.隐式解析可选类型(推荐)
在定义时使用!代替?,使用可选变量时可以不用!解析,
若可选变量为nil则崩溃
var x:Int!=10 var y=x+1 // x后不用写!了
7.可选变量默认为nil var x:Int? x为nil
8.var str:String?=Optional.Some("sb") == var str:String?="sb"
var str:String?=Optional.None == var str:String?=nil
9.可空链(若链上某点为nil时停止后续调用)
例: a?.b?.c?.d c为nil则不在后续调用
10. 可选绑定
例:
if x!=nil {
print(“\(x)”)
}else{
print(“\(x)为空”)
}
等价于 称为【可选绑定】(判断可选值是否有值)
if let a=x{ x不为nil时为真
print(“\(a)”)
}
if var a=x{ x不为nil时为真
print(“\(a)”)
}
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
[类型检查和转换操作符]
is 用来检查 实例是否属于某类或遵守某协议,结果为bool
as 用来转换到 一个子类或协议
as? 可选转换 若转换为可选失败,返回nil (用来转换到 一个子类或协议)
as! 可选转换 若转换为可选失败,崩 (用来转换到 一个子类或协议)
var x=1.5 as Int x为Int
[嵌套类型]
struct Cycle{
struct Point{
var x:Double
var y:Double
}
var center:Point
var radius:Double
}
var cycle=Cycle(center:Cycle.Point(10,10),radius:10)
var point:Cycle.Point=Cycle.Point(10,10)
[Any AnyObject]
Swift 为不确定类型提供了两种特殊的类型别名:
Any 可以表示任何类型,包括函数类型、可选类型。
AnyObject 可以表示任何类类型的实例。
[可选链]
可以应用于任意类型
当前值可能为nil的可选值上请求和调用属性、方法及下标。
如果可选值有值(类型为可选返回类型),那么调用就会成功;如果可选值是nil,那么调用将返回nil。
x?[0].play()
[泛型]
Array、Dcitionary使用了泛型
==泛型函数
func xswap(inout a:T,inout b:T){
let tmp=a
a=b
b=tmp
}
==泛型类型 扩展时可不写 ,如:extension Array{}
struct Matrix{ // 泛型约束:遵守协议
var arr:[T]
var arr2:[M]
}
==泛型下标
subscript(indices: Indices) -> [Item]
where indices.Iterator.Element == Int {
var result = [Item]()
for index in indices {
result.append(self[index])
}
return result
}
类型约束
在一个类型参数名后面放置一个类名或者协议名,并用冒号进行分隔,来定义类型约束
【关联类型】
定义一个协议时,声明一个或多个关联类型作为协议定义的一部分。关联类型为协议中的某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。
你可以通过 associatedtype 关键字来指定关联类型。
protocol Person{
associatedtype T
associatedtype T2: Protocol
associatedtype T3: Protocol where T3.Element == T
var id:T{get set}
}
protocol PersonProtocol: Person where T: Protocol {
}
func allItemsMatch (_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
}
2.1.1 整型
var x=1,y=-1
Int 64位有符号 大小同当前系统的字长相等
UInt 大小同当前系统的字长相等
Int8 8位有符号 最大值: 127
UInt8 最大值: 255 unsigned char
Int16 short
UInt16 unsigned short
Int32 int
UInt32 unsigned int
Int64 long
UInt64 unsigned long
常用
10 推断为 Int
3.14 推断为Double
005==5 // 可在十进制前加0
1000==10_00 // 下划线不影响原值
十进制 :10
二进制 :0b000001
八进制 :0o7
十六进制 :0x5A
Int(arc4random())%3 : 随机数(UInt32)范围012
sizeof(Int) : 8字节 大小同当前系统的字长相等,尽量使用Int保持代码互通
Int.max 最大值 // 根据当前系统(32位、64位)决定
Int.min 最小值
var a:Float = 1.5/2 正确(编译器将2转为float)
var a:Float = 1.5/Int(2) 错误(此处 /两边必须是Float)
var a:Int = 5.0 as Int 正确
浮点数判断相等 if (a-b>-0.000001 && a-b<0.000001) { // a和b才绝对相等}
M_PI 3.1415…
^不是次方的意思
三角形:
cosA=(pow(b,2)+pow(c,2)-pow(a,2))/(2*b*c)
pow(cosA,2)+pow(sinA,2)=1
2*b*c*sinA 面积
A=acos(cosA)
fabs() : 求绝对值
pow( 2.0 , 3 ) : 求次方。2的3次方,不能有运算符(否则结果不对)
sqrt(Double类型) : 求平方根。
2.1.2 浮点型
var f:Float=1.2222 32位 Float 的精度为小数点后6位
var d:Double=1.222222 64位 Double 的精度为小数点后12位
1.25e-2 == 1.25*10^-2 // e或E 表示10^
0x5p2 == 5*10^2 // p或P 表示10^
2.1.3 bool型
真和假(逻辑值类型,只能是true或false)
var b1:Bool=true // 注意打印出来不是1,是true
var b2:Bool=false
注意:”非0为真“在Swifit中不适用
2.1.4 元组
将多个不同类型(任意类型)的值组合成一个复合值
用于函数返回值,不适合复杂的数据结构
// 没有别名 取值
var t:(Int,Float,String)=(1,1.0,“sb”)
t.0 t.1 t.2
// 有别名 取值
var t:(age:Int,name:String)=(1,”sb”)
var t:(age:Int,name:String)=(1,”sb”)
t.age t.name
//
let http404error=(404,”Not Found”)
let (f,t)=http404error // 获取值
let (f,_)=http404error // 获取部分值
let (x,y)=(3,4) // 获取值
[(Int,String)] 数组元素类型为(Int,String)元组类型
var a:() = () 其中a是Void类型 正确
2.1.5 字符型(Character)、字符串型(String)
字符型
双引号括起来的字符(不能为字符串 )
var c:Character="你"
let dollarSign = "\u{24}"
let catCharacters: [Character] = ["C", "a", "t", "!", ""]
let catString = String(catCharacters)
常见Unicode值:
A:65 a:97
Z:90 z:122
0:48 9:57
判断相等:
“a” == “a”
转义字符:
\0 (null字符),\\ 反斜杠, \t 水平tab,\n 换行,\r 回车,\” 双引号,\’ 单引号,\unn 2位十六进制,\unnnn 4位十六进制,\Unnnnnnnn8位十六进制
字符串型
字符串是字符(Unicode的任意字符)的有序集合。
字符串中的每个字符所占的空间不同,因此不能使用下标,只能用Index。
String基于 Unicode 标量, Unicode 标量是对应字符或者修饰符的唯一的21位数字,例如U+0061表示小写的拉丁字母("a")。
String和NSString(2者可以通过 as 无缝桥接)的区别:
a.String是值类型(结构体类型) ---值拷贝,NSString是引用类型(类类型)
b.String不能用整数来遍历,而NSString可以用整数来遍历
c.String可以被修改,NSString不可以修改
d.无缝桥接
var str1:NSString
var str2:String
str1=str2 as String 或 str1=str2
str2=str1 as NSString 或 str2=str1
var str3:NSString?=str2 as? NSString
字符串: a b c 越界
s.startIndex s.endIndex
->s.startIndex.successor()->
s.endIndex.predecessor<-
根据Index来查询:str[str.startIndex]。
根据整数来查询: str[str.startIndex.advancedBy(0)]
如果String是空串,startIndex和endIndex是相等的
str[str.index(before: greeting.endIndex)]
str[str.index(after: greeting.startIndex)]
str[str.index(str.startIndex, offsetBy: 7)]
for index in str.indices {
print("\(str[index]) ", terminator: "")
}
字面量
// 字符串字面量,推断为String类型
var s1="hello"
// 多行字符串字面量,此时的s2==s1,开头"""的后面、结束"""的前面没有换行
var s2="""
hello
"""
// s2="hellohello2",\是续行符
var s2="""
hello\
hello2
"""
// 多行字符串字面常量中可不需要\"转义
var s3="""
hello\"
hello2
"""
字符串插值
// 括号中的表达式不能包含非转义反斜杠 (\)、回车、换行符
var s2="world"
var s1="hello \(s2)"
字符串类型操作
创建、查询、添加、修改、删除
[创建]
// 创建空字符串
var str=String() 或 var str:String=""
// 创建
var str:String="你好"
var str = [String](count: 3, repeatedValue: "x")
var str = String(format:“%d”,2)
var str:String="\u{41}\u{42}" // \u{十六进制unicode值}
var str:String=“\(str)”
[查询]
// 字符串比较
str==str2
str!=str2
str>str2 // 从左至右比较字符的ASCII码
// 是否 长度为0
str.isEmpty
// 是否 有前缀、后缀
str.hasPrefix(“abc”) str.hasSuffix(“abc”)
// 是否 包含
str.containsString("d")
// 字符串长度
str.characters.count
// count属性返回的字符数量并不总是与包含相同字符的NSString的length属性相同。NSString的length属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。
// 注意不修改本身,返回的是修改的字符串
// 首字母转大写(将每一个分割的字符串)
var s2=str.capitalizedString
// 转小写
var s2=str.lowercaseString
// 转大写
var s2=str.uppercaseString
// 字符串反转
var s2=String(str.characters.reverse())
// 转数组
var arr="ss,ddff,ssdd".componentsSeparatedByString(",")
// range转index
(range?.startIndex)!
struct Range {
var startIndex: T
var endIndex: T
}
// txt转str
var str=try! String(contentsOfFile: "/Users/xiaozai/Desktop/dict.txt")
// utf编码值
str.utf8 // UInt8 (无符号8位)
str.utf16 // UInt16 (无符号16位)
str.unicodeScalars // c.value UInt32
// 获取Unicode标量集合
// s2[s2.startIndex].value为Unicode的10进制值(对应21位数值,UTF-32编码,UInt32)
var s2=s1.unicodeScalars
for u in s2{
u.value 66
}
for scalar in "hello".unicodeScalars {
print("\(scalar) ") // h e l l o
}
for character in "hello" {
print(character)
}
for c in s2.characters{ // s2.characters—>字符arr
c
}
for i in 0..生成的是可选值
s1.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
// 网址百分号编码还原—>生成的是可选值
s1.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
将汉字转为拼音
扩展字符串
extension String {
// 【将汉字转为拼音】
func transformToPinYin() -> String {
let mutableString = NSMutableString(string: self)
// 把汉字转为拼音
CFStringTransform(mutableString, nil, kCFStringTransformToLatin, false)
// 去掉拼音的音标
CFStringTransform(mutableString, nil, kCFStringTransformStripDiacritics, false)
let string = String(mutableString)
return string.stringByReplacingOccurrencesOfString(" ", withString: "") // 去掉空格
}
}
字符串MD5、SHA1加密
《1》创建一个swift和OC桥接的文件,引入头文件
#import
《2》扩展String类
extension String {
func Md5() -> String {
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = CUnsignedInt(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = Int(CC_MD5_DIGEST_LENGTH)
let result = UnsafeMutablePointer.alloc(digestLen)
CC_MD5(str!, strLen, result)
let hash = NSMutableString()
for i in 0.. String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
for byte in digest {
output.appendFormat("%02x", byte)
}
return output as String
}
}
2.1.6 集合类型
Array、Dictionary、Set (存储的变量是同一类型,会将不同类型推断为同一公约类型)
高级语言中,数组和字典都采用哈希映射。
一个类型为了存储在集合中,该类型必须是可哈希化的。基本数据类型默认可哈希化(a.hashValue Int型)。
自定义类型需要遵守Hashable协议(提供一个类型为Int的可读属性hashValue)。因为Hashable协议符合Equatable协议,必须提供一个"是否相等"运算符(==)的实现。a == a(自反性)、a == b意味着b == a(对称性)、a == b && b == c意味着a == c(传递性)
NSArray和NSDictionary取出的元素是NSObject类型的,所以必须强转。
[1,[1,2,3]] 数组中的数组默认是NSArray类型的,解决:
personDataArr=[["Z",NSMutableArray.init(array: ["张一","张二","张三"])],["L",NSMutableArray.init(array: ["李四","李一","李二"])]]
NSArray/NSMutableArray 和 Array 用 as 无缝桥接 (尽量NSArray:少拷贝)
区别:
a.NSArray不可变,Array由let、var决定
b.NSArray 是引用类型,是类类型,Array是值类型,是结构体类型
NSArray
【千万注意:NSArray为引用类型,函数传递时传的是指针,在函数中要NSMutableArray.init(array:array)复制一份,防止二次调用函数时又将array修改了】
arr.count
var arr=NSArray(objects:”sb”,123,”bb”)
var arr=[1,2,3] as NSArray
arr.containsObject(3) // 是否 包含元素3
arr.subarrayWithRange(NSRange(location:1,length:2)) // 截取 返回Array
for obj in arr{} // 遍历
for i in 0..str 以_拼接每个元素
str.componetsSeparatedByString(“=”) // str—>arr 以=分割字符串
数组
存储一组同一类型(元素类型可以是不同类型,但被编译器统一推断为同一类型--类型的公约数)、逻辑上有序的值(可以相同)。
创建数组,[]重置已知类型的数组-类型不变
var arr:[Int]=[]
var arr=[]
var arr=Array()
var arr=[String]()
//
var arr=Array(count: 3, repeatedValue: 2.2)
var arr=[1,2,3.0] 推断为Double型
var arr=[1,”hello”,2.0] 推断为NSObject型
var arr:[Any]=[1,”hello”,2.0] 推断为Any型
var arr:[Int,Float]=[3,3.0] 错误 []中只能写一种类型
var arr:[Int]=[1,2,3]
等价于
var arr:Array=[1,2,3]
var arr=[1,2,3]
等价于
var arr:Array=[10,20]
根据下标获取数据
arr[0]
数组长度
arr.count
是否为空
arr.isEmpty
数组不可变:大小不可变,值可变。字典不可变:大小不可变,键值对不可变。
数组的内置方法和属性:
arr.first arr.last arr.count arr.isEmpty
arr.capacity // 无需重新分配内存时数组最多可以存多少个元素 arr.reserveCapacity(10)
arr.sortInPlace { $0 < $1} 从小到大 重新修改arr
arr.reverse() 输出数组反转,原数组不变
arr.filter{$0>2} 输出>2的数组,原数组不变
arr.map {$0 > 0 ? -$0 : 1 } 返回每个元素被map这个函数操作后的结果
arr.reduce(0) {$0 + $1 } 返回一个值(转为单一类型)
二维数组:
var arr:[[Int]]=[[1,2],[1,2]]
for i in 0..arr[j]{
var tmp=arr[j+1]
arr[j+1]=arr[j]
arr[j]=tmp
}
}
}
选择排序(打擂台:依次从第一个数与其他数作比较,交换)
for i in 0..arr[j]{
var tmp=arr[i]
arr[i]=arr[j]
arr[j]=arr[i]
}
}
}
插入排序(循环依次将最大值取出放在新数组中)
var arr=[34,11,5,76,22,88]
var arr2:[Int]=[]
for x in 0.. arr[indexMax]{
indexMax=i+1
}
}
arr2.append(arr.removeAtIndex(indexMax))
}
插入排序(第二种方式:从选择排序衍生)
var arr=[22,12,44,2,14,60]
for i in 0..
NSDictionary、NSMutableDictionary 和 Dictionary 用 as 无缝桥接
NSDictionary
var dic=NSDictionary(dictionaryLiteral:(“key”,”value”),(“key2”,”value2”)) // print打印格式不一样{} []
var dic1=dict as Dictionary
dic.allKeys // 所有键
dic.allValues // 所有值
for (key,value) in dic{}
for obj in dic{}
// 数组枚举器
var enu=arr.objectEnumerator() var enu=arr.reverseObjectEnumerator()逆序
while true{
let obj = enu.nextObject()
if obj==nil{
break
}
print(obj)
}
// 字典枚举器 遍历同上
var end:NSEnumerator=dic.keyEnumerator() // key
var enu=dic.objectEnumerator() // value
NSMutableDictionary
var mdic=NSMutableDictionary(“s”:”sb”,”b”:”bb”)
mdic.setObject(“ssbb”,forKey:”ss”) // 添加
mdic[“bb”]=“bbbb” // 添加或替换
mdic.removeObjectForKey(4) // 删除
var obj=mdic[5] // 返回可空
字典
存储一组相同类型的值,每个值被对应唯一的键(键必须是可离散化类型)无序。
[创建]
var dic:[String:String]=[:]
var dic=[String:String]()
var dic:Dictionary=[:]
var dic=Dictionary()
var dic:Dictionary=Dictionary()
var dic=[String:String](minimumCapacity: 10)
var dic = [ “name”: “sb”, “age”: “1”]
var dic = NSDictionary(dictionary: ["name":"sb","age":"1"])
[查]
// 大小
dic.count
// 是否为空
dic.isEmpty
// 根据键获取值
dic[“sb”] // 存在则返回值,不存在则返回nil
dic[“不存在的键”] // 键不存在则返回nil
// 所有键,所有值
dic.keys dic.values
// 将所有key->数组
let arr=Array(dic.keys) 或 var keys=[String](dic.keys) var values=[String](dic.values)
// 更新或添加 字典不可以相加合并
dic[“s”]=“sb” 存在替换,不存在则添加(存在返回旧值,不存在返回nil) 或
dic.updateValue("sb", forKey: "ss") 存在返回旧值,不存在返回nil
// 删除
dic.removeValueForKey("d") 存在返回旧值,不存在返回nil 或
dic[“d”]=nil
// 遍历
for (key,value) in dic{}
for obj in dic{ obj.0 obj.1}
for keyHello in dic.keys { // dic.values
print(" \(dic[keyHello])")
}
arr.joinWithSeparator("/") 数组变字符串
let airportCodes = [String](airports.keys)
let airportNames = [String](airports.values)
Set
存储相同类型并且没有确定顺序的值(每个元素只出现一次)
// 创建
var letters = Set()
var strs: Set = ["H", "H1", "H2"]
var strs: Set = ["H", "H1", "H2"] // Set必须写
// 置空(类型不变)
letters = []
// 大小
letters.count
// 判空
letters.isEmpty
// 是否包含
letters.contains("Funk")
// 遍历
for genre in letters {
print("\(genre)")
}
// letters是否是letters2的子集
letters.isSubset(of: letters2)
// letters是否是letters2的父集
letters.isSuperset(of: letters2)
// letters是否和letters2有交集
letters.isDisjoint(with: letters2)
// letters是否和letters2元素一样
letters== letters2
// 添加
letters.insert("a")
// 删除(存在返回该值,不存在返回nil)
if let removedGenre = letters.remove("Rock") {
print("\(removedGenre)")
} else {
print("no have")
}
// 排序
letters.sorted()
// 组合(创建一个新Set)
letters.union(letters2)
// 将相同的值组合(创建一个新Set)
letters.intersection(letters2)
// 将不同的值组合(创建一个新Set)
letters. symmetricDifference(letters2)
// 去掉letter2中的值组合(创建一个新Set)
letters.subtracting(letters2)
2.1.7 枚举
一组有限个相关的共同类型的值(该类型声明的变量 只能取该集合的某个值)
本质:结构体类型
枚举成员在被创建时不会被赋予一个默认的整型值(即不会0、1....)。
使用
// 1 Color有3个成员
enum Color{ // 大写开头
case blue="h"
case gray,purple
}
var c1=Color.blue // 枚举值(可以是字符串,整型,浮点型)
var c2:Color=.blue // 当已经被推断为Color类型,可以不写枚举名
// 2
enum Color{
case a(String) // 关联一个注释
case b(start:Int,end:Int) // 关联一个元组
}
Color c=Color.b(start: 10, end: 12)
print("\(c.start)")
// 3
switch c{
case .blue
case .red
}
// 4
enum Color:Int{ // + :Int (若未给第一个a赋值,则默认0,其后+1)
case a=1
case b,c
}
var color=Color.init(rawValue:1) 返回的是可选类型
var color=Color(rawValue:1)
print(Color.a) // 打印a
print(Color.a.rawValue) // 打印0 原始值不可以是任意类型,原始值自类型定义之后不可修改
enum Color:String{
case blue
case gray
}
Color.blue.rawValue=="blue"
[递归枚举]
有一个或多个枚举成员使用该枚举类型的实例作为关联值
enum Color:String{
indirect case blue(Color c)
case gray
}
indirect enum Color:String{
case blue(Color c)
case gray
}
2.1.8 类和结构体
类和结构体的比较
[相同点]
1、属性(用于存储值)
2、方法(用于实现功能)
3、下标(用于访问属性、方法)
4、构造器(用于初始化)
5、可扩展
6、可遵守协议
[类比结构体多]
1、继承
2、类型转换
3、析构器
4、引用计数
[定义]
// 首字母大写
class Person{
var name:String
var personS=PersonS()
}
struct PersonS{
var high:Int // 若结构体实例为常量,初始化之后无法修改
let length:Int // 初始化之后无法修改
}
// 实例化 直接():属性均会被初始化为默认值
var person=Person()
var personS=PersonS()
// (结构体会自动生成逐一构造器),类没有
var personS=PersonS(high:1,length:3)
// 访问属性、方法
person.name person.personS.high
personS.high
结构体用于:
封装简单的相关数据值,且需值拷贝时,且结构体中的元素也是值拷贝,不需要从已有类型集成属性或行为。(Swift中的数组,字典)
类用于:
需引用拷贝时。(绝大多数使用类而非结构体)
指针和C略不同:不是直接指向地址;不需要使用*创建引用
类
引用类型
类的实例称为对象。
判断地址是否相同【是否引用同一个实体】使用:(恒等运算符) === !==
实例?.属性 :整个表达式为可选值,若实例为nil则整个表达式无效。
类(或结构体)不需要创建.h和.m文件,仅需创建一个文件(自动创建接口文件)
class Person{
var name:String=“xiaoming”
func desc()->String{
return “My name is xiaoming”
}
init(name:String){ // 注意:类中没有默认构造器,必须自定义
self.name=name
}
}
var person=Person()
person.name
person.desc()
通过 class 类名{变量和方法声明同前} 声明类
通过 类名() 实例化
通过 . 调用属性和方法
[属性]
和类型(类、枚举、结构体)相关联的常量或变量。
用于存取,分为3种:
1、存储属性 (仅由类和结构体) (默认值或初始化 中设置,除lazy属性、可选属性外必须设置)
2、计算属性(必须是变量) (可以类、结构体、枚举) 变量也可以计算变量和监视。
3、类型属性 (和类型相关联,而不是和实例)
1、存储属性(在内存中有实体)
常量的所有属性会变成常量。
2、计算属性(在内存中没有实体,通过计算获得)
用于同步
var other{
set(name){
// 简写:set{ name=newValue }
// 只读计算属性(变量)没有set方法,设值之后无法改变
person.name=“cb”
person.age=1
}
get{ // 没有set方法时可省略get{} : var other{ return ”sc”}
let x1=1
let x2=2
return x1+x2
}
}
不能仅有set(报错),有set就得有get
3、类型属性
系统会保证其只会被初始化一次、延迟初始化(但不需要lazy)
a.必须在定义 存储 类型属性时,赋一个默认值(不能在构造器中初始化)
b.对于值类型 ,可以定义 存储和计算 类型属性;
c.对于引用类型,只能定义 存储 类型属性;
值类型 属性:struct P{static var age=1}
引用类型 属性:class P{static var age=1} 不能用class修饰,不支持(仅支持方法)
取:P.age 存:P.age=1
[延迟赋值]
lazy var str2:String="ddd” 添加lazy可延迟赋值,用到时才赋值(防止内存过高)。
用于:
1、属性一开始不适合初始化
2、长时间用不上,而且占内存
[属性监视器]
监视存储属性,值发生改变时调用
(注意:默认值和初始化时 不调用监视器)
(注意:不能监视计算属性,set、get已经在监视)
父类的属性在子类的构造器中被赋值时,它在父类中的 willSet 和 didSet 观察器会被调用,随后才会调用子类的观察器。在父类初始化方法调用之前,子类给属性赋值时,观察器不会被调用。
将属性通过 in-out 方式传入函数,willSet 和 didSet 也会调用。因为 in-out 参数采用了拷入拷出模式:即在函数内部使用的是参数的 copy,函数结束后,又对参数重新赋值。
var age:Int{
willSet(newAge){ // 值存储前调用。 参名默认newValue(新值)
}
didSet{ // 值存储后调用。 参名默认oldValue(旧值)
if age>oldValue {
age=oldValue // 则改为旧值,不会引起再次监视
}
}
}
[全局变量/局部变量]
全局变量是在函数、方法、闭包或任何类型之外定义的变量。
局部变量是在函数、方法或闭包内部定义的变量。
计算属性和属性观察器也可以用于全局变量和局部变量。
全局的常量或变量都是延迟计算的,但不需要标记lazy修饰符。
[方法]
和类型相关联的函数,分为实例方法和类型方法
可以为 类、结构体、枚举 设置方法
实例方法:
被具体实例调用 person.func() self代表该实例
实例方法在内存中初始化一次,放在代码区
类型方法:
被类型调用 Person.func() self代表该类型
加static,类中也可以(加class:可以被重写,static(final class)不可被重写)
在类型方法中,可以不写类型名直接调用其他类型方法或属性
注意:
1、值类型(结构体、枚举)的属性默认不能在实例方法中修改,若要修改:mutating func func1{}在方法名前加mutating。[但值类型常量不能]
2、
enum T{
case A,B
mutating func func1{
self=A // 枚举中self可以设置为某个枚举值
}
}
3、类型方法{}和类型计算属性{}
调用self或类型
.类型方法或属性
不能调用实例方法或属性
实例方法{}和实例计算属性{}和构造器{} 直接调用或调用self(属性名相同)
self.实例方法或属性
能调用类型方法或属性(类型.方法或属性)
[self]
每个实例都有一个,self=实例自身
一般情况下不写,在形参和属性名相同时:self.age=age。
否则出错age=age 表示形参=形参
注意:不能用self.实例方法为属性设置默认值(self此时还不能用,没初始化)
[下标]
用于访问集合(字典、数组:已经实现)、列表、序列、自定义类型。
一般情况下不需实现。
class Person{
不能输入输出inout,不能默认值。可以重载。
subscript(index:Int)->Int{ // 形参:任意数量、任意类型。 返回:任意类型。
get{
}
set(newValue){ // 注意:若返回类型为可选,需加newValue!
}
}
}
使用:person[3]
例2:
class a {
subscript(c:Int)->Int{
print(c)
return 0
}
}
var b = a()
b[2] // 输出为2。这是因为下标函数被调用。
print(b[2]) // 输出为2然后输出0。这是因为下标函数的返回值为0.
[继承]
子类继承超类的所有属性、方法、其他特性(下标)
允许调用超类中的属性和方法(super.属性或方法:区别子类和父类的方法、属性)和覆写(加override否则报错)。
调用实例的方法、属性先从本类中查找,找不到再从超类中查找,依次...
不允许多重继承。
不继承任何类的类称为基类,Swift中的类并不从通用的类继承。
子类的构造器默认不继承父类,只有在没有不可空属性+没有构造器时继承
class Child:Person{
init(){
super.init()
}
}
注意:
1、只读类型会被覆盖为读写类型。若父属性有set则必须同时set、get。
2、覆写超类的常量属性、只读计算属性,不可以设置属性监视器。
3、不可以同时设置setget和监视器
4、添加final可防止被覆写 final var str:String="" final func m()(final类不可以被继承)
5、值类型不可以继承
6、覆写override[只可以升权,不可以降权]
存储属性 —> 计算属性(不能还为存储属性,没意义) (不能为只读,权力小了) 或 监视器(限制:常量不行,lazy属性不行)
计算属性 —> 计算属性(不能为存储属性,不能降权) 或 监视器
只读计算属性 —> 可读写计算属性 或 只读计算属性(不可以将可读写继承为只读)
7、在类的实例方法中,super代表子类继承父类的部分(本质上还是属于子类的部分 )
8、计算型类型属性使用class重写,不能重写存储计算属性
[初始化]
必须为存储属性(除了lazy属性、可选属性)设置初始值,通过以下2种方法来实现:
1、设置默认值
2、在构造器中设置值(常量也可以,但不能在子类中修改)。
(二者都不会调用监视器。如果一个属性始终一个值则最好在声明时默认值)
[1、设置默认值]
let someProperty: Int=3
// 可以传入闭包,创建实例则赋值(不能调用其他属性-还未初始化或方法)
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
[2、构造器]
类提供2种构造器:
1、指定构造器(最主要的构造器)
类必须有指定构造器(不写任何构造器时存在默认,新添构造器后默认消失)
init(参数){}
2、便捷构造器
convenience init(参数){
self.init(参数) // 便捷构造器中必须调用一个指定构造器
}
构造器链:
指定构造器必须调用直接超类的构造器;
便捷构造器只能调用本类的其他构造器;便捷构造器必须调用指定构造器作为结束。
任何构造器都可以完成初始化。
[构造器初始化分为两步](初始化值->修改值),并提供检查机制:
1、指定构造器确保 本类所有(新添)属性在调用超类构造器之前初始化
2、指定构造器确保 继承属性在调用超类构造器之后赋值
3、便捷构造器确保 调用本类其他构造器之后为(新添或继承)属性赋值
4、初始化第一阶段完成前:
[1、不能调用实例方法;2、不能super.属性 ]
第一阶段:
0.调用指定构造器或便捷构造器
[实例 分配内存,但未完全初始化]
1.指定构造器 初始化本类中所有新添属性(1)
2.指定构造器调用超类构造器 初始化继承部分(2) (顺着继承链向上到结束)
[实例 内存初始化完成]
第二阶段
[可以访问super.超类属性、能调用实例方法]
0.到达顶端后向下返回,修改继承属性(3)
1.便捷构造器加工实例,修改新添属性(4或无)。
[构造器]
用于初始化实例
默认构造器:
// 在没有不可空属性和构造器时默认存在
// 也可自定义init(){} 使用:var p=Person()
// 结构体还有逐一构造器
init(){}
自定义:
init(name:String){ self.name=name} // person(name:"hello")
init(_name:String){ self.name=name} // person("hello")
[构造器的继承]
a. 若指定构造器的参数类型一致需要在前加override,便捷构造器则不需要
b. 超类属性必须由超类构造器初始化(super.init(…)),不能super.属性=值。
c. super.init()之前 设置子类新添的属性(第一阶段)
d. super.init()之后 才能调用超类属性(这样才能有效)
e. super.init()在不使用父类属性+父类没有构造器时可省略(大部分情况要写)。
f. 不能调用父类便利构造器
[可失败构造器]
1、init?(){}
2、init!(){} 构造失败则崩溃
有可能构造失败的构造器,构造失败返回nil。
参数类型不能和其他构造器一致。
构造器本身是不需要返回的,当返回nil则表示创建失败,不返回则表示创建成功
子类可以重写可失败为非可失败,相反则不行。
可以调用其他构造器。
带原始值的枚举类型会自带一个可失败构造器init?(rawValue:)
[必要构造器]
若超类 required init(){}
则其子类、子类的子类、…都必须required init(){} ,不用override
[构造器代理:构造器相互调用]
构造器可以调用其他构造器完成部分初始化工作,这一过程称为构造器代理。(防止递归)
_指定构造器_必须要调用_超类指定构造器_(向上代理)
(不能调用本类中其他便捷构造器) (super.指定构造器)
_便捷构造器_必须要调用_本类指定构造器_(横向代理)
(可以调用本类中其他便捷构造器)
(self.其他构造器,init前+convenience)
[值类型的构造器代理]
值类型 自定义了构造器后,默认的构造器将无效,可以自己写上init(){}。
值类型没有继承,只能调用自己的其他构造器。
init(age:Int){} init(age:Int,name:String){self.init(age)}
[构造器的自动继承]
重写一个父类的指定构造器时,总是需要写override修饰符
"重写"一个父类便利构造器时,不需要加override前缀
a.若子类没有定义构造器,会自动继承超类的所有构造器。[前提:属性都有默认值]
b.若仅实现部分指定构造器,则不继承其他构造器
c.若子类重写超类所有指定构造器, 会自动继承超类的所有便捷构造器。[不需前提]
[析构器]
deinit{}
a、析构器可以访问所有属性(实例的析构器被调用后,实例才会被释放)。
b、类实例释放前自动调用析构器(做额外操作),不允许手动调用(仅适用于类)。
c、仅能有一个析构器 deinit{} 无参无()
d、Swift使用ARC自动引用计数来管理内存自动释放,对于自定义类则需手动释放。
e、子类默认继承父类的析构器并自动调用,即使没有定义子类析构器。在子类的deist中不能写super.deinit ,默认已经自动调用。
NSObject是OC写的
let pClass=NSClassFromString(“项目名.Person”) // 将字符串转为Class类,不继承自NSObject
obj.isMemberOfClass(pClass!){ // 判断对象是否是 此类实例
}
obj.isKindOfClass(pClass!){ // 判断对象是否是 此类型或子类型
}
obj.responseToSelector(方法名) // 是否有此方法
默认构造器init(){}
自定义构造器
// 可以仅初始化没有默认值的不可空存储属性
// 使用时:类型名(属性名:值)
init(age:Int){self.age=age}
类型名.init(属性名:值)
注意:
1、自定义构造器的第一个形参也有外部形参名。
2、可选类型属性默认值自动为nil
3、常量属性只能默认值或在构造器中(可以)修改,初始化后不允许改变
4、结构体的所有属性都有默认值时,提供默认构造器Person(),否则提供 Person(每一个属性:赋值)
[使用闭包或函数设置属性的默认值]
var age:Int={()->Int in return 10}()
简化 var age:Int={100}()
var age:Int=func1() // 必须是类型方法,不能为实例方法(此时self不能用)
结构体
将多个数据和方法组合在一起的一种数据类型。(含构造器)
struct 结构体名{
var 属性名:标注类型 实例属性
…
func 方法名(){} 实例方法
…
}
[逐一成员构造器]
默认存在(属性有序),自定义初始化方法init(){}之后,默认构造器则失效
结构体名(属性名:值…) 或 结构体名.init(属性名:值…)
例:
struct Person{
var name:String
var age:Int
func desc()->String{
return “描述”
}
mutating func func1(){
age=10
}
// 结构体(值类型)的实例方法默认不能改变结构体的属性,需要用关键字mutating定义变异方法用来改变结构体的属性
}
var person=Person(name:”sb”,age:1) // 使用默认构造器
var person=Person() // 使用它时,结构体中的不可选属性必须有默认值
person.name
// 当结构体中的元素是引用类型时,拷贝的是指针.
结构体和类的区别:
结构体:值类型(结构体无继承),类:引用类型。
结构体类型的实例属性 不能再是本结构体类型的变量。类类型的实列属性 可以是本类类型的变量???
用结构体和一维数组构造二维数组和三维数组
// 二维
struct MatrixArr{
var arr:[Int]
var cols:Int
var rows:Int
init(rows:Int,cols:Int,defaultValue:Int){
self.rows=rows
self.cols=cols
self.arr=[Int](count: rows*cols, repeatedValue: defaultValue)
}
subscript(row:Int,col:Int)->Int{
set{
arr[row*self.cols+col]=newValue
}
get{
return arr[row*self.cols+col]
}
}
}
var mArr=MatrixArr(rows: 10, cols: 2, defaultValue: 1)
mArr[2,1]=12
print(mArr[2,1])
// 三维
struct SanArr{
var arr:[Int]
var cols:Int
var rows:Int
var highs:Int
init(rows:Int,cols:Int,highs:Int,defaultValue:Int){
self.rows=rows
self.cols=cols
self.highs=highs
self.arr=[Int](count: rows*cols*highs, repeatedValue: 1)
}
subscript(row:Int,col:Int,high:Int)->Int{
set{
arr[high*rows*cols+row*cols+col]=newValue
}
get{
return arr[high*rows*cols+row*cols+col]
}
}
}
var sanArr=SanArr(rows: 5, cols: 4, highs: 3, defaultValue: 1)
sanArr[2,2,1]=10
print(sanArr[2,2,1])
2.2 运算符
一个特殊的符号或短语:用来检查、改变或组合值.
【基本运算符】
[分类]
(1)算数运算符 + - * / % 会检测并不允许溢出
(2)关系运算符 > >= < <= == != 返回Bool
(3)逻辑运算符 && || ! 注意短路
(4)条件运算符 条件?A:B 条件为真则执行A,否则B
(5)赋值运算符 = += -= /= *= 没有返回值
还可分为:一元运算符、二元运算符、三元运算符
[注意]
(1)整数操作为 整数;浮点型操作为 浮点型
(2)a!=b有歧义,应写为 a != b 一定要注意!=前后都要有空格
(3)符号左右要么都有空格,要么都没空格,否则出错。
(4)var x=y=5 错, 运算表达式没有值!!!
(5)8.5%2.5==1.0 正确, Swift允许对浮点数取余
(6)var a:Float=1.5/Int(2) 错, 强制转换后/运算符两边必须同类型
var a:Float=1.5/2 正确,编译器自动转换2为Float
(7)swift语法支持重载原生的运算符,也支持自定义的运算符
(8)位运算符是操作数据在内存中的补码,所以它的操作数必须是整数类型
(9)+ 可拼接字符串
(10)a % b 等于 a%-b ,即:b的符号会被忽略 。-10%3等于-1
(11)如果两个元组的元素相同,且长度相同,且可以被比较(如:BOOL不能用<)的话,元组就可以被比较,从前到后依次比较直到不等或比较结束。Swift 标准库只能比较七个以内元素的元组比较函数。
(1, "zebra") < (2, "apple") // true,因为 1 小于 2
(3, "apple") < (3, "bird") // true,因为 3 等于 3,但是 apple 小于 bird
[运算符优先级]
括号成员第一 () [] .
全体单目第二 ! ~
乘除余三加减四
条件高于赋值 ?: =
逗号运算级最低
【溢出运算符】
&+、&-、&*、&/、&%
有效位截断(除0溢出为 0)
(无符号:值上溢出为最小 0、值下溢出为最大 全1)
(有符号:值上溢出为-最大,值下溢出为-1)
3&/0==0 为true (Swift规定)
【位运算符】
用于:图像处理、设备驱动、通信协议等底层开发中(可单独操作原始数据的比特位)
原理:对补码进行操作—>再将补码变原码
~ 按位取反(每位取反,和数之间不能有空格)
var x:Int8 = 2
var y = ~x
& 位与(都1则1)
var x:Int8=8
var y:Int8=6
var z = x & y
| 位或(有1则1)
var z = x | y
^ 按位异或(不同则1)
var z = x ^ y
<< 左移(所有向左移)
var z = x << 1 等价于 x*2^1
>> 右移(所有向右移)
var z = x >> 1 等价于 x/(2^1)
无符号移位,移出抛弃、空白补0
有符号移位,移出抛弃、右移空白补符号、左移符号位不变空白补0
【空合运算符】
a ?? b a没有值时等于b,否则忽略b。
b类型需和a一致,a必须为可选类型。
【区间运算符】
闭区间运算符
a...b 从 a 到 b(包括 a 和 b)的所有值的区间
半开区间运算符
a..
【自定义运算符】/ = - + * % < >!& | ^ ~
需要先声明、实现,再使用
3修饰符:
前 prefix
中 infix (infix不写)
后 postfix 优先级高于前缀
例1:
声明:prefix operator func ^_^{}
实现:prefix func ^_^(p:Point)->Point{
return Point(x:-p.x,y:-p.y)
}
使用:^_^p
例2:
结合性(associativity)的值默认为 none,优先级(precedence)默认为 100
声明:infix operator ~~{associativity left precedence 140}
实现:func ~~(p1:Point,p2:Point){}
使用:point1 ~~ point2 ~~ point3
设置优先级(中缀运算符)
infix operator +-: AdditionPrecedence // 默认:优先级在三元运算符之上
【运算符重载】
将已有的运算符重新定义函数
不能对默认的赋值运算符(=)进行重载,只有组合赋值运算符可以被重载。
不能对三目条件运算符 (a ? b : c) 进行重载。
func +(p1:Point,p2:Point)->Point{
return Point(x:p1.x+p2.x,y:p1.y+p2.y)
}
func - (a:Int,b:Int)->String{
return String(a+b) + "重载成功"
}
postfix func ++ (inout i:Int)->Int{
println("重载成功")
return i+10
}
extension Person {
static func == (left: String, right: String) -> Bool {
return left == right
}
static func != (left: String, right: String) -> Bool {
return !(left == right)
}
}
单目、双目、+=、!==
前缀表达式:~2、!2
后缀表达式:
try表达式:try method() 、try! method()、try? method()
二元表达式:a+b
赋值表达式:x=3 无返回值
字面常量表达式: “abc”
self表达式
super表达式
闭包表达式
选择器表达式:#selector(run)
下标脚本表达式:
2.3 控制流
控制程序执行流程 的语法(并不能提升运算效率)
循环可嵌套,各控制流可嵌套
分类
1、顺序执行——按顺序执行
2、条件执行——按条件执行
if
条件必须是BOOL类型,不能为赋值表达式(不返回值),不能直接为变量(不允许隐式和0比较)。条件或循环变量 两边的括号可以省略(if后需要空格),但大括号不能省。
如果条件满足则执行:if 条件{}
如果条件满足执行A,否则执行B:if 条件{A}else{B}
如果条件A满足则执行代码A,条件B满足则执行代码B,类推至都不满足则执行代码X: if 条件A{A}else if 条件B{B} …else{X}。
guard
条件为真则继续执行,否则进入else。(必须有else分支)
guard let name = person["name"] else {
}
switch
必须穷尽,否则崩溃
switch支持所有数据类型、多种匹配(相等匹配,区间匹配,绑定后条件匹配);不需要写break,会自动跳出(即分支不贯穿)。fallthrough关键字可以贯穿到下一分支;
switch a{
case 1: 相等匹配,默认类型与a相同
case 2…4: 区间匹配
case 5..<7: 左闭右开区间匹配
case (2,3):
case (_,0): _ 匹配任何值
case (let x,0): 同上但可以单独获取x值
case ”hello”:
case “A”,”B”:
case let x where x.hasSuffix(“C”): 绑定后条件匹配 后缀
case let x where x.hasPrefix(“C”): 前缀
case let x where x<9:
default:
}
3、循环执行
for var i=0;i<10;i++ {
}
for i in 0..<3{
}
for _ in 1…3{ // 若不使用变量可用_代替
}
// 0,2,4,6,8(从0到10间隔为2,不包含10)
for tickMark in stride(from: 0, to: 10, by: 2) {
}
// 0,2,4,6,8,10(从0到10间隔为2,包含10)
for tickMark in stride(from: 0, through: 10, by: 2) {
}
while x<10 {
}
repeat{ // 先无条件执行一次
}while x<10
// for-in可以运用在(字符串、arr、dic)
// 数组
for score in scores{
}
// 字典
for (k,v) in dic{
}
控制转移语句:
continue、break、fallthrough、return、throw
可以设置标签,然后continue 标签或 break 标签。例:
label:
for i in 0…2{
break label
}
判断系统类型:
if #available(iOS 10, *) { // iOS10以上执行
} else {
}
2.4 函数和闭包
函数
包装一段代码,用于实现某个功能。可以多次调用(提高代码复用率)。
函数类型也是一种类型,函数类型变量。
函数可嵌套,嵌套函数可以访问外部函数的变量。
若函数有返回值,可以不用变量来接住这个返回值。
在main.swift文件中的代码都会最终被放入main函数,即本文件任何处都在main函数内。
[函数的声明]
func 函数名(形参:类型,…) -> 返回类型{
// 参数长度可以可变 (arrs:Int…), 函数名(1,2,3) 只能有一个且放在最后
// 参数类型可以是 (arr:Int[],fun2:Int->Bool)
// 返回类型可以是 (Int,Int) , 例返回(6,7)
// 返回类型可以是 (Int)->Int
// 函数参数默认是常量,输入输出参数:使用inout修饰变量形参,不能有默认值。调用时加&, func(&count)。
// 定义了返回类型,则必须返回,否则编译时错误
// 返回类型可以起别名 ,使用: 函数名(参数).别名
// 可以无形参,无返回值(相当于返回void或())
// 参数可以设置默认值,调用时可不写 形参:类型=默认值 需放在最后面???
// 形参默认是常量,var age:Int设为变量
// 形参作用域仅限于本函数
}
例:
func fun1(name:String,pwd:String) ->Bool{}
func func1(){} 等价于 func func1()->void{}
func func1(name:String,age:Int=3){}
// 年龄可以不写(有默认值)
func1(“sb”)
// 注意:默认首个参数只有内部名,其他参数有内外部名。
func1(“sb”,age:1)
// 可以添加 _ 来取消外部名,使用#来添加相同外部名
func func1(name:String , _ age:Int)
// 外部名 内部名
func func1(name2 name:String, age2 age:Int) func1(name2:”sb”,age2:10)
// y不需要再定义元组成员名
func func1()->(min:Int, max:Int){} let y=func1(); y.min y.max
[函数的调用]
var success:Bool=fun1(“xiaoming”,”123”)
[函数变量]
// func1的[函数类型(由参数类型和返回类型组成)]是:(Int)->Bool
var func1:(Int)->Bool{}
// func2的函数类型:()->()或()->void 没有返回类型时默认:空元组
var func2(){}
// 数组元素类型为函数类型
[(Int)->Bool]
[函数回调]
函数回调:将函数指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数:一个通过函数指针调用的函数.
[Swift类型] 分为2类:(1)值类型(2)引用类型
值类型:
实体复制时(赋值、传参时不改变原值、返回值)为值拷贝(不可以继承)。
整型、字符串型、数组类型、字典型、结构体、枚举。
引用类型:
实体复制时为指针拷贝。
闭包、函数类型、类类型
注意:
1、引用类型的常量(不能修改指向的对象),但可以修改其所指向的对象的属性;值类型常量则不可以。
2、func func1(inout array:[Int]){} func1(&arr) 此处传的是指针。在形参前加inout表示传入指针
inout不能有默认值,不能修饰可变形参,不能用let和var修饰
[函数嵌套]
func func1(){
func func2(){
} // 此时func2未被调用,可修改外部函数变量
func2() // 此时调用,也可以在外部调用
}
[函数递归]
0.必须有一个终止条件
1.调用函数本身
例:
// 求平方根的自定义函数(系统提供:sqrt(4))
func xxsqrt(num:UInt) ->Double{
var x1=0.0
var x2=1.0
while x1 != x2{
x1=x2
x2=(x1+Double(num)/x1)/2
}
return x1
}
// 改为递归
func xxxsqrt(x1:Double)->Double{
let x2=(x1+Double(n)/x1)/2
if x1==x2 {
return x2
}else{
return xxxsqrt(x2)
}
}
xxxsqrt(1) // 第一个近似值是1
闭包
自包含的功能模块(可以在代码中被定义和使用)即一个代码块。
引用类型。
函数是特殊的闭包。
闭包可以用于2个页面的数据传递和方法回调。
[三种形式]
1、全局函数(有名字,不能捕获值) 即函数
2、嵌套函数(有名字,能捕获外函数的变量) 即嵌套函数
3.、闭包表达式(无名字,能捕获其上下文中变量或常量) Swift 会管理在捕获过程中涉及到的内存操作。
[闭包表达式]
{ (参数列表) -> 返回类型 in
函数体
}
闭包变量:
var col1={(a:Int,b:Int)->Int in return a+b}
col1被推断为:(Int,Int)->Int (和函数类型一样)
var col1={(a:Int,b:Int)->Int in return a+b}(3,5) 结果为8
var col2=col1 引用同一个
参数类型:
常量、变量和 inout 类型、可变参数 、元组,不能提供默认值
[闭包表达式的简化原则]
1、利用上下文推断参数和返回值类型 (因为闭包是作为函数的参数进行传入的,Swift 可以根据参数类型(不是闭包的参数)推断闭包的参数和返回值的类型)
2、单行闭包可省略 return 关键字
3、参数名简写(Swift 通过 $0,$1,$2 引用闭包的参数值,可以省略参数和 in) sort(names) { $0 > $1 }
4、Trailing 尾随闭包(当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面;如果函数只需要闭包表达式一个参数,当您使用 trailing 闭包时,可把 () 省略掉:
sort(){ $0 > $1 } 简化为: sort{ $0 > $1}
例:
func func1(s1:String,s2:String) -> Bool {
return s1 > s2
}
sort(arr, func1)
转为闭包则为
sort(arr, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
根据优化1优化后则为
sort(names, { s1, s2 in return s1 > s2 } )
再根据优化2优化后则为
sort(names, { s1, s2 in s1 > s2 } )
再根据优化3优化后则为
sort(names, { $0 > $1 } )
特殊:根据 Swift 的 String 类型定义了关于大于号 (>) 的字符串实现,让其作为一个函数接受两个 String 类型的参数并返回 Bool 类型的值。再优化为:sort(names, >)
[循环引用]
闭包分配给实例的属性,且该闭包捕获了该实例(通过指向该实例或其它成员),引起循环引用。
解决:捕获列表
[匿名闭包]
arrs.map({
(arr:Int) -> Int in // in 将参数、返回值 和 内容分隔开
let x=3*arr
return x
})
[自动闭包]
可以用来延迟执行某些步骤
let x = { 10 } // 没有参数,返回其中的表达式的值。
let xx=x()
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// 标识其接受自动闭包作为参数(可以直接传 字符串)
func serve(customer customerProvider:@autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
[逃逸闭包]
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。
@escaping
必须显式地引用self : self.age
// 冒泡函数升级
func xxsort(inout arr:[Int],cmp:(Int,Int)->Bool){
for i in 0..$1}) // 从大到小
// 正确
var x={}
// unowned self
{[unowned self] () -> () in
print("加载") // self 皆为unowned self
}
// 起别名
typealias AsyncFunc = (()->Void) -> Void
func concat(left : AsyncFunc , right : AsyncFunc) -> AsyncFunc{
return { complete in
left{
right{
complete()
}
}
}
}
2.5 协议
———为了弥补swift中没有多重继承,为代理设计模式提供了语法保障。
规范了一套接口(定义了一系列属性、方法、构造器,但不能实现,可扩展可继承)。
a. 类、枚举、结构体都可以使用协议
// 类专属协议(只有类可遵守,当代理设置为weak则必须+)
protocol Protocol:class{}
b. optional :不一定要实现
还必须在protocol前+@obj:升级为类协议,结构体不能遵守了
c. 协议作为类型:
var array:[Protocol] 遵守此协议才能添加
可作为变量 常量 属性、函数 方法 构造器的参数 返回值、Array Dictionary Set的元素类型
d. 用final修饰的类:实现协议的构造器可不写required,否则必须加required。
e. 给协议扩展时,方法需实现(扩展内的),构造器需convenience(类时)
f. 构造器可失败
g. 协议合并protocol<协议1,协议2>
h. 协议要求未被完全满足,在编译时会报错。协议名大写开头
// 协议
// 加 :class则表示类专属协议。class要在其他协议之前
protocol ProtocolOne:class{
// 总是用 var 关键字来声明
// get、set则不能实现为常量或只读计算属性;set则可实现get方法
var dic:String{get set}
// 在协议中定义类型属性时,总是使用 static。实现可以class
static var dic:String{get set}
// 不支持为协议中的方法的参数提供默认值
// 可以使用 static、 class
func eat()
// mutating:方法内部可修改实例
mutating func funcOne()
// 构造器,实现时必须加required
// 使用 required 修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。如果类已经被标记为 final,则不需要。
// 如果父类(未遵守而是定义)和协议同时定义了,子类遵守协议时需加required override。
init(someParameter: Int)
// optional:可不实现。只能被类遵循。
// 协议和可选方法都要加@objc
// 修饰的方法、属性为可选类型,调用x.play?()
@objc optional func play()
}
// 遵守协议(有超类则超类必须放在最前)
class Person: SuperClass,ProtocolOne, ProtocolTwo{
var dic:String=“abc”
func eat(){
}
func funOne(){
dic+=“def”
}
}
// 扩展协议,为遵循协议的类型提供实现(属性、方法以及下标)
// 当多个扩展都实现了,则限制条件最多的那个协议扩展优先级最高
extension ProtocolOne{
func play(){
print("hello");
}
}
// 为协议扩展 添加限制条件(扩展集合,其元素必须遵循ProtocolOne协议时)
extension Collection where Iterator.Element: ProtocolOne {
}
// 是否符合该协议
BOOL is=person is ProtocolOne
// 转换为遵守协议的类型
person as? ProtocolOne
person as! ProtocolOne
2.6 扩展
为已有的类型、协议、结构体、枚举添加功能:
方法(实例方法、类方法、构造器)
属性(不能为实例存储属性)
遵守协议
下标
extension Int{
// 可以嵌套类型
enum X{
case A,B,C
}
var x:X
// 下标
subscript(x: Int) -> Int {
return x+10
}
// self:实例或类型
func neg() -> Int{return -self}
// 内部修改了实例
mutating func square(){
}
}
var a=1
print(a.neg())
注意:
1、可添加 计算属性。不能添加 实例存储属性,不能为已有属性添加属性观察器。
2、可添加 初始化方法
3、修改值类型的方法加mutating关键字
4、extension遵从协议 举例:extension Int:Protocol1{
中的方法要实现
}
5、不能重写本类中的方法,但可以重写父类的方法,所以可以出现override
- 设计模式
工程模块化(在工程中组织对象与对象之间的关系)
模块内高内聚、模块间松耦合(便于管理、维护、移植)
3.1. 代理模式(delegate)
一对一(一方为委托方、一方为代理方)
用于:1.让别人去做 2.传值,接收返回值
实现:
创建协议。
代理方(遵守协议,做具体实现) 并作为委托方的一个属性;
在委托方内部调用代理方的方法。
protocol HelpProtocol:NSObjectProtocol{
func clean(){}
func cook(){}
}
class BaoMu: HelpProtocol{
func clean(){}
func cook(){}
}
class Person:NSObject{
weak var baoMu: HelpProtocol?
func clean(){
baoMu!.clean()
}
func cook(){
baoMu!.cook()
}
}
注意:
1、weak修饰的delegate遵守的协议必须+:class
2、可在扩展中去实现(无法修改原码时;方便维护)。extension VC:协议{具体实现}
3、vc.delegate=self 或 vc.delegate=vc2 as! 协议
- 内存管理
[自动引用计数ARC]
Swift使用ARC管理内存
引用计数仅用于引用类型(类),不能用于值类型(值拷贝,没法再指向它)。
创建时引用计数置1,被引用则加1,失引用则减1,0则销毁。(弱引用、无主引用不加减)
强引用
可能循环引用:两个实例互相引用.内存无法被释放导致内存泄漏
防止循环引用:
弱引用: 加weak 必须是可选类型。weak var tenant: Person?
无主引用:加unowned 必须是非可选类型。不能赋值nil,销毁后不为nil但使用它则崩溃(野指针)。 unowned let country: Country
[闭包引起的强循环引用]
闭包内使用self的成员,就要用self.someProperty(必须加self.)
实例调用闭包,闭包中又引用self。
解决:捕获列表
捕获列表中的每一项都由一对元素组成。一个元素是weak或unowned关键字,另一个元素是类实例的引用(例如self)或初始化过的变量(如delegate = self.delegate!)。
例:
var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// 这里是闭包的函数体
}
- 访问控制
访问控制可以
限定其它源文件或模块中的代码对你的代码的访问级别。
隐藏代码的一些实现细节,为其他人访问和使用的代码提供接口。
可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数
访问控制模型基于模块和源文件。
模块:一个独立的代码单元(一个框架或一个程序),使用import导入。
源文件:属于某个模块。
五种
Open(级别最高) 和 Public 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。
Open 只能作用于类和类的成员,它和 Public 的区别如下:
Public 或者其它更严访问级别的类,只能在其定义的模块内部被继承,Open 也可以在引用它的模块中被继承。
Public 或者其它更严访问级别的类成员,只能在其定义的模块内部的子类中重写,Open也可以在引用它的模块中的子类重写。
Internal (默认)让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。
File-private 限制实体只能在其定义的文件内部访问
Private(级别最低) 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问
遵循一个基本原则:不可以在某个实体中定义访问级别更低(更严格)的实体
例:
public class SomePublicClass {}
类型
一个类型的访问级别也会影响到类型成员(属性、方法、构造器、下标)的默认访问级别:
1、如果你将类型指定为 private 或者 fileprivate 级别,那么该类型的所有成员的默认访问级别也会变成 private 或者 fileprivate 级别。
2、如果你将类型指定为public 或者 internal (或者不明确指定访问级别,而使用默认的 internal ),那么该类型的所有成员的默认访问级别将是内部访问。
元组
元组的访问级别将由元组中访问级别最严格的类型来决定(自动推断,不能显示指定)。
函数
函数的访问级别根据访问级别最严格的参数类型或返回类型的访问级别来决定。但是,如果这种访问级别不符合函数定义所在环境的默认访问级别,那么就需要明确地指定该函数的访问级别(例如:默认环境是internal,而返回类型是private,则必须显式声明为private类型)。
枚举
枚举成员的访问级别和该枚举类型相同,不能为枚举成员单独指定不同的访问级别。
枚举定义中的任何原始值或关联值的类型的访问级别至少不能低于枚举类型的访问级别。
嵌套类型
如果在 private 的类型中定义嵌套类型,那么该嵌套类型就自动拥有 private 访问级别。如果在 public 或者 internal 级别的类型中定义嵌套类型,那么该嵌套类型自动拥有 internal 访问级别。
子类
子类的访问级别不得高于父类的访问级别。
可以通过重写为继承来的类成员提供更高的访问级别。
常量、变量、属性、下标
常量、变量、属性不能拥有比它们的类型更高的访问级别。
常量、变量、属性、下标的 Getters 和 Setters 的访问级别和它们所属类型的访问级别相同。
Setter 的访问级别可以低于对应的 Getter 的访问级别,
fileprivate(set) var num
public private(set) var numberOfEdits = 0
构造器
自定义构造器的访问级别可以低于或等于其所属类型的访问级别。唯一的例外是必要构造器,它的访问级别必须和所属类型的访问级别相同。
默认构造器的访问级别与所属类型的访问级别相同,除非类型的访问级别是 public。如果一个类型被指定为 public 级别,那么默认构造器的访问级别将为 internal。
结构体默认的成员逐一构造器
如果结构体中任意存储型属性的访问级别为 private,那么该结构体默认的成员逐一构造器的访问级别就是 private。否则,这种构造器的访问级别依然是 internal。
协议
协议中的每一个要求都具有和该协议相同的访问级别。
如果你定义了一个 public 访问级别的协议,那么该协议的所有实现也会是 public 访问级别(注意此点不同于其他)。
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。
一个类型可以采纳比自身访问级别低的协议。采纳了协议的类型的访问级别取它本身和所采纳协议两者间最低的访问级别。
Extension
Extension 的成员具有和原始类型成员一致的访问级别。可以明确指定 extension 的访问级别。
使用 extension 来遵循协议的话,就不能显式地声明 extension 的访问级别。extension 每个 protocol 要求的实现都默认使用 protocol 的访问级别。
扩展同一文件内的类,结构体或者枚举,extension 里的代码会表现得跟声明在原类型里的一模一样。也就是说你可以这样:
在类型的声明里声明一个私有成员,在同一文件的 extension 里访问。
在 extension 里声明一个私有成员,在同一文件的另一个 extension 里访问。
在 extension 里声明一个私有成员,在同一文件的类型声明里访问。
泛型
泛型类型或泛型函数的访问级别取决于泛型类型或泛型函数本身的访问级别,还需结合类型参数的类型约束的访问级别,根据这些访问级别中的最低访问级别来确定。
类型别名
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。
类型别名的访问级别不可高于其表示的类型的访问级别。
测试模块需要访问程序模块 Build Options -> Enable Testability
- 内存安全(待详看)
多线程同时访问会造成同样的情况,但这里访问冲突的讨论是在单线程。
默认情况下,Swift 会阻止你代码里不安全的行为。例如,Swift 会保证变量在使用之前就完成初始化,在内存被回收之后就无法被访问,并且数组的索引会做越界检查。
Swift 也保证同时访问同一块内存时不会冲突,通过约束代码里对于存储地址的写操作,去获取那一块内存的访问独占权。因为 Swift 自动管理内存,所以大部分时候你完全不需要考虑内存访问的事情。
内存访问的冲突会发生在你的代码尝试同时访问同一个存储地址的时侯。
至少有一个是写访问、它们访问的是同一个存储地址、它们的访问在时间线上部分重叠
如果一个访问不可能在其访问期间被其它代码访问,那么就是一个瞬时访问。基于这个特性,两个瞬时访问是不可能同时发生。大多数内存访问都是瞬时的。
In-Out 参数的访问冲突
一个函数会对它所有的 in-out 参数进行长期写访问
in-out 参数的写访问会在所有非 in-out 参数处理完之后开始,直到函数执行完毕为止。如果有多个 in-out 参数,则写访问开始的顺序与参数的顺序一致
你不能在原变量以 in-out 形式传入后访问原变量
var stepSize = 1
func increment(_ number: inout Int) {
number += stepSize
}
increment(&stepSize)// 错误:读和写访问重叠了,就此产生了冲突。
往同一个函数的多个 in-out 参数里传入同一个变量也会产生冲突
方法里 self 的访问冲突
mutating func shareHealth(with teammate: inout Player) {
balance(&teammate.health, &health)
}
shareHealth(player) // 会冲突
shareHealth(player2) // 不会冲突
属性的访问冲突
结构体,元组和枚举的类型都是由多个独立的值组成的。因为它们都是值类型,修改值的任何一部分都是对于整个值的修改,意味着其中一个属性的读或写访问都需要访问整一个值。
var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
balance(_:_:) 需要在调用期间对2个参数发起写访问(重叠了,造成冲突)。
大多数对于结构体属性的访问都会安全的重叠。改为本地变量而非全局变量,编译器就会可以保证这个重叠访问时安全。
遵循下面的原则(保证结构体属性的重叠访问安全)
你访问的是实例的存储属性,而不是计算属性或类的属性
结构体是本地变量的值,而非全局变量
结构体要么没有被闭包捕获,要么只被非逃逸闭包捕获了