关于iOS Coding过程中经常遇到各种小问题或是一些疑惑,整理收集如下.

基本语法:

定义变量和常量: let 和
var常量和变量的命名:你可以使用任何你喜欢的字符作为常量和变量名,包括
Unicode 字符:

let π = 3.14159let 你好 = "你好世界"let ?? = "dogcow"

常量与变量名不能包含数学符号,箭头,保留的Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字

注意:如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名,你可以使用反引号将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。

注意:是指向的对象不可以再进行修改.但是可以通过指针获得对象后,修改对象内部的属性在真实使用过程中,建议先定义常量,如果需要修改再修改为变量

// 注意:声明为常量不可以修改的意思是指针不可以再指向其他对象.但是可以通过指针拿到对象,修改其中的属性// view : UIView = [[UIView alloc] init];// Swift对象中不需要*var view : UIView = UIView()view = UIView()let view1 :UIView = UIView(frame: CGRect.init(x: 10, y: 10, width: 100, height: 100))//view1 = UIView()view1.backgroundColor = UIColor.blueColor()let button : UIButton = UIButton(type: UIButtonType.ContactAdd)button.backgroundColor = UIColor.brownColor()button.setTitle("anniu", forState: UIControlState.Normal)button.center = CGPoint(x: 20, y: 20)view1.addSubview

在OC开发中,如果一个变量暂时不使用,可以赋值为0或者赋值为空在swift开发中,nil也是一个特殊的类型.因为和真实的类型不匹配是不能赋值的(swift是强语言)但是开发中赋值nil,在所难免.因此推出了可选类型

可选类型的写法:

// 写法一:定义可选类型let string : Optional<String> = nil// 写法二:定义可选类型,语法糖let string : String? = nil

使用可选类型(optionals)来处理值可能缺失的情况;格式是在类型后面加?:var num Int?

使用隐式解析可选类型(optionals)来处理值在第一次赋值之后就确定之后一直有值;格式是在类型后面加!:var num Int!

使用这些关键字后面都必须跟大括号;

// 传统写法for var i = 0; i < 10; i++ { print}for i in 1...5 { print}//如果不需要用到范围中的值,可以使用下划线_进行忽略for _ in 1...5 { print}

while和do while循环

while循环while的判断句必须有正确的真假,没有非0即真while后面的()可以省略

var a = 0while a < 10 { a++}

do while循环使用repeat关键字来代替了do

let b = 0repeat { print b++} while b < 20

switch 语句在 swift 和 c 中的区别:

  • 在C语言中:

1.如果 case 的结尾没有 break就会接着执行下一个 case 语句;

  • 在 swift 中:

1.不需要在每个 case 后面增加 break, 执行完 case
对应的代码后默认会自动退出 switch 语句;2.每个 case
语句后面都必须有可执行的语句;3.case
后面可以跟多条件匹配和范围匹配;4.case 可以匹配元组5.case
可以支持多种数据类型6.switch语句可以使用 where
来增加判断条件7.值绑定注意:
switch语句必须保证处理所有可能的情况,不然编译器就会直接报错,因此在这里的
default 一定要加上去;

let somePoint = switch somePoint {case : print is at the origin")case : print("(\(somePoint.0), 0) is on the x-axis")case : print("(0, \(somePoint.1)) is on the y-axis")case (-2...2, -2...2): print("(\(somePoint.0), \(somePoint.1)) is inside the box")default: print("(\(somePoint.0), \(somePoint.1)) is outside of the box")}// 输出 " is inside the box"

值绑定

let anotherPoint = switch anotherPoint {case : print("on the x-axis with an x value of \case : print("on the y-axis with a y value of \case let : print("somewhere else at , \}// 输出 "on the x-axis with an x value of 2"

fallthrough :执行完当前的 case 会直接执行 fallthrough 后面的 case
语句或者 default 语句;注意: fallthrough后面的 case
条件不能定义变量和常量标签;

let num = 0switch num {case 0: fallthroughcase 1: printdefault: print}

if分支语句和OC中if语句有一定的区别

判断句可以不加()在Swift的判断句中必须有明确的真假不再有非0即真必须有明确的Bool值Bool有两个取值:false/true

guard是Swift2.0新增的语法它与if语句非常类似,它设计的目的是提高程序的可读性guard语句必须带有else语句,它的语法如下:

当条件表达式为true时候跳过else语句中的内容,执行语句组内容条件表达式为false时候执行else语句中的内容,跳转语句一般是return、break、continue和throwguard
条件表达式 else {// 条换语句break}语句组

OC和Swift中字符串的区别

在OC中字符串类型时NSString,在Swift中字符串类型是StringOC中字符串@””,Swift中字符串””

使用 String 的原因

String 是一个结构体,性能更高NSString 是一个 OC 对象,性能略差String
支持直接遍历Swift 提供了 String 和 NSString 之间的无缝转换

字符串的基本使用:

// 字符串遍历var str = "Hello, Swift"for c in str.characters { print}//字符串拼接let str1 = "Hello"let str2 = "World"let str3 = str1 + str2//字符串和其他类型拼接let name = " John"let age = 18let info = "my name is \, age is \"//字符串的格式化let min = 3let second = 4let time = String(format: "%02d:%02d", arguments: [min, second])

字符串的截取

Swift中提供了特殊的截取方式

let greeting = "hello swift!"greeting[greeting.startIndex]// hgreeting[greeting.endIndex.predecessor()]// !greeting[greeting.startIndex.successor()]// elet index = greeting.startIndex.advancedBygreeting[index]// w//通过索引遍历字符串for index in greeting.characters.indices { print("\(greeting[index]) ", terminator: " ")}// 打印输出 "G u t e n T a g !"//插入删除字符串 var welcome = "hello" welcome.insert("!", atIndex: welcome.endIndex)// welcome 现在等于 "hello!"welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor// welcome 现在等于 "hello there!"welcome.removeAtIndex(welcome.endIndex.predecessor// welcome 现在等于 "hello there"let range = welcome.endIndex.advancedBy..<welcome.endIndexwelcome.removeRange// welcome 现在等于 "hello"

简单的方式是将String转成NSString来使用在标识符后加:as NSString即可

let myStr = "www.baidu.com"var subStr = (myStr as NSString).substringFromIndexsubStr = (myStr as NSString).substringToIndexsubStr = (myStr as NSString).substringWithRange(NSRange(location: 4, length: 5))

ca88会员入口 1数组,字典,集合

声明的数组,字典需要进行初始化才能使用,数组类型往往是在声明的同时进行初始化的

数组合并:只有相同类型的数组才能合并,不建议一个数组中存放多种类型的数据数组的基本操作

var shoppingList = ["Eggs", "Milk"]print("The shopping list contains \(shoppingList.count) items.")// 输出 "The shopping list contains 2 items."if shoppingList.isEmpty { print("The shopping list is empty.")} else { print("The shopping list is not empty.")}// 打印 "The shopping list is not empty."(shoppinglist 不是空的)shoppingList.append// shoppingList 现在有3个数据项shoppingList += ["Baking Powder"]// shoppingList 现在有四项了shoppingList += ["Chocolate Spread", "Cheese", "Butter"]// shoppingList 现在有七项了var firstItem = shoppingList[0]// 第一项是 "Eggs"shoppingList[0] = "Six eggs"// 其中的第一项现在是 "Six eggs" 而不是 "Eggs"shoppingList[4...6] = ["Bananas", "Apples"]// shoppingList 现在有6项shoppingList.insert("Maple Syrup", atIndex: 0)// shoppingList 现在有7项// "Maple Syrup" 现在是这个列表中的第一项let mapleSyrup = shoppingList.removeAtIndex// 索引值为0的数据项被移除// shoppingList 现在只有6项,而且不包括 Maple Syrup// mapleSyrup 常量的值等于被移除数据项的值 "Maple Syrup"let apples = shoppingList.removeLast()// 数组的最后一项被移除了// shoppingList 现在只有5项,不包括 cheese// apples 常量的值现在等于 "Apples" 字符串for item in shoppingList { print}// Six eggs// Milk// Flour// Baking Powder// Bananasfor (index, value) in shoppingList.enumerate() { print("Item \(String(index + 1)): \}// Item 1: Six eggs// Item 2: Milk// Item 3: Flour// Item 4: Baking Powder// Item 5: Bananas

字典和并:需要遍历一个字典,然后将遍历出的 key,value 添加到第二个字典中

集合的基本操作:

ca88会员入口 2Set的基本操作ca88会员入口 3集合之间的成员关系

元组:元组类型是由 N 个任意类型的数据组成;例如:var point = (x:10,
y:20)/var point = 取元组中的元素: point.x/point.y
point.0/point.1如果使用 let 定义一个元组是常量,是不能够修改的;

ca88会员入口 4函数.png

函数的定义格式:

func <#name#>(<#parameters#>) -> <#return type#> { <#function body#>}//func是关键字,多个参数列表之间可以用逗号分隔,也可以没有参数//使用箭头“->”指向返回值类型//如果函数没有返回值,返回值为Void.并且“-> 返回值类型”部分可以省略

函数的使用注意

  • 注意一:
    外部参数和内部参数在函数内部可以看到的参数,就是内部参数在函数外面可以看到的参数,就是外部参数默认情况下,从第二个参数开始,参数名称既是内部参数也是外部参数如果第一个参数也想要有外部参数,可以设置标签:在变量名前加标签即可如果不想要外部参数,可以在参数名称前加_
  • 注意二: 默认参数某些情况,如果没有传入具体的参数,可以使用默认参数
  • 注意三:
    可变参数swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数它们必须具有相同的类型我们可以通过在参数类型名后面加入的方式来指示这是可变参数一个函数最多只能有一个可变参数。
  • 注意四:
    引用类型默认情况下,函数的参数是值传递.如果想改变外面的变量,则需要传递变量的地址必须是变量,因为需要在内部改变其值Swift提供的inout关键字就可以实现

对比下列两个函数

// 函数一:值传递//注意:在 swift3.0中已经不推荐这样写func swap(var a : Int, var b : Int) { let temp = a; a = b; b = temp}var a = 10var b = 20swapprint, b:\// 函数二:指针的传递func swap1(inout a : Int, inout b : Int) { let temp = a a = b b = temp print, b:\}swap1(&a, b: &b)print, b:\

输入输出参数:
inout使用注意:1.传递参数时,必须在实参的前面加上的&2.不能传入常量或者字面量作为参数值(因为它们都是不可以修改的)3.输入输出参数不能有默认值4.输入输出参数不能是可变参数5.输入输出参数不饿能够在使用
let,var 修饰(inout 和 let/var 不能共存)

总结:输入输出参数不能有默认值,而且可变参数不能用 inout 标记。如果你用
inout 标记一个参数,这个参数不能被 var 或者 let 标记。

输入输出参数可以实现函数的多返回值(其实让函数返回元组类型也能实现多返回值)

函数类型:函数类型也是数据类型的一种,有形式参数和返回值类型;

///定义一个叫mathFunction的变量,类型是‘一个有两个Int 型的参数并返回一个Int 型的值的函函数”。并让这个新变量指向addTwoInts函数var mathFunction:  -> Int = addTwoInts

函数类型作为参数类型

//包含三个参数mathFunction :  -> Int, a : Int, b : Intfunc printMathResult(mathFunction:  -> Int, _ a: Int, _ b: Int) { print("Result: \(mathFunction}printMathResult(addTwoInts, 3, 5)

函数类型作为返回类型

func stepForward(input: Int) -> Int { return input + 1}func stepBackward(input: Int) -> Int { return input - 1}}//返回值是一个 -> Int类型的函数func chooseStepFunction(backwards: Bool) ->  -> Int { return backwards ? stepBackward : stepForward}

函数重载:函数名相同,函数类型不同;

// 方法的重载:方法名称相同,但是参数不同,可以称之为方法的重载func ride(num1: Int, _ num2 :Int , _ num3 :Int) -> Int { return num1 * num2 * num3}

方法是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与
Objective-C 中的类方法(class methods)相似。结构体和枚举能够定义方法是
Swift 与 C/Objective-C 的主要区别之一。在 Objective-C
中,类是唯一能定义方法的类型。但在 Swift
中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型上定义方法。

方法就是函数,只是这个函数和某个类型相关联;

一、引言

       
函数是有特定功能的代码段,函数会有一个特定的名称调用时来使用。Swift提供了十分灵活的方式来创建与调用函数。事实上在Swift,每个函数都是一种类型,这种类型由参数和返回值来决定。Swift和Objective-C的一大区别就在于Swift中的函数可以进行嵌套。

       
而Swift中的闭包是有一定功能的代码块,这十分类似于Objective-C中的block语法。Swift中的闭包语法风格十分简洁,其作用和函数的作用相似。

Coding Tip Collection [Via 500px]

实例方法

实例方法是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致;

二、函数的创建和调用

       
函数通过函数名,参数和返回值来定义,参数和返回值决定一个函数的类型,在调用函数时,使用函数名来进行调用,示例如下:

ca88会员入口 5

图例1

       也可以创建没有参数的函数:

ca88会员入口 6

图例2

       
 上面介绍的函数类型都比较常见,对于多返回值的函数,在Objective-C中十分难处理,开发者通常会采用字典、数组等集合方式或者干脆使用block回调,在Swift中,可以使用元组作为函数的返回值,示例如下:

ca88会员入口 7

图例3

          也可以是函数返回一个Optional类型的值,支持返回nil,示例如下:

ca88会员入口 8

图例4

       
 在函数的参数名前,开发者还可以再为其添加一个参数名称作为外部参数名,示例如下:

ca88会员入口 9

图例5

       
其实Swift函数中的参数列表有这样一个特点,除了第一个参数外,之后的参数都默认添加一个一个和内部名称相同的外部名称,如果开发者不想使用这个外部名称,使用_符号设置,示例如下:(新版本的swift已做更改跟OC同步,不过“_”依旧有效可用)

ca88会员入口 10

图例6

         
Swift也支持开发者为函数的参数创建一个默认值,如果函数的某个参数有设置默认值,则开发者在调用时可以省略此参数,示例如下:

ca88会员入口 11

图例7

       
 还有一种情形在Objective-C中也很难处理,对于参数数量不定的函数,在前面章节介绍过,Objective-C一般会使用list指针来完成,在Swift中编写这样的函数十分简单,示例如下:

ca88会员入口 12

图例8

       
Swift中参数默认是常量,在函数中是不能修改外部传入参数的值的,如果有需求,需要将参数声明成inout类型,示例如下:(新版本下inout类型声明到参数后,参数类型Int前)

ca88会员入口 13

图例9

缺省参数

今天在写一个UILabel生成方法时,突然想到去年刚开始接触Object-C时就曾一直疑惑,语言层级特性(语法糖)是否支持类似C#中缺省参数的功能?

缺省参数(Default Argument
Value),顾名思义,就是在声明函数的某个参数的时候为之指定一个默认值.
在调用该方法时要是传入了这个参数,则使用传入的值,如果缺少这个输入参数,那么直接使用设定的默认值进行调用.
例如在C#语法中声明一个缺省参数的方法:

缺省参数定义[C#]

调用方法时自由地选择采用缺省的参数值,或者覆盖参数缺省值:

调用方式[Via C#]

在方法声明和调用时极大简化语法,代码写起来短小精悍,具有较高的自由性同时代码也易读理解.但其实Object-C语法层面本身并不支持缺省参数,自然没法能够直接实现类似C#语言中的写法.但真的无法实现吗?虽然语法层面不能直接支持,其实是可以的只是有点绕.

笨一点的方式定义方法时声明该参数,在方法实现时内部处理默认值.类似:

方式一[Object – C]

如上我们定义一个UILabel,默认文字颜色是红色,但不是每次都会定义,
那在定义方法时做一个内部约定,当defineColor传入为nil,就是取其默认值为红色,如果为非nil时则正常赋值.
这种写法偏向于在方法内定义了一个约定,逻辑简单,但在方法声明层面上如果不知道隐藏潜在约定代码逻辑或者没有很好注释说明,对调用者来说跟defineColor和orginStr两个参数并没有什么区别的.难道没有更好的方式吗?
有.

方式二[Objec – C]

改良一下替代的方法,其实是定义两个方法,
一个方法声明时包含所有缺省参数,用另外一个则不包含缺省参数方法调用前者即可.
这种方式优点将使用默认值的参数放在函数的参数列表的末尾写法,
确保了所有调用函数的非默认参数使用相同的顺序,
并明确地表示在每种情况下相同的函数调用.  在声明层面能够很好区分方法实现,
而不存在额外的歧义.

这种折中的方式缺点是同一个功能实现多了一个不同的入口,
一定程度增加代码的复杂度. Objective-C
由于语法的特点几乎无法在不大幅改动的情况下很好地实现缺省参数.Objective-C
社区诟病很多年的这一特性,
而作为其的继任者Swift语言是否能够在语法上友好的支持? 答案是自然肯定的.

Playground中实现[Swift]

如上可见Swift能够完美的实现方法的缺省参数.但如果在深入一点,你会有惊喜发现.在C#中我们知道方法实现缺省参数并没有问题,而不要忘了语法上还有一个限制条件:

调用时你只能从最后一个参数开始进行省略,
如果你要省略一个参数,你必须省略它后面所有的参数,即:带缺省值的参数必须放在参数表的最后面.

类似如果我在C#这样写法:

缺省参数声明顺序[C#]

缺省参数声明是写在最前面,
编译器会直接报错,而相反Swift更为自由,则没有类似C#这样语法限制,如果同样语法在Swift则是可以编译通过的,如下:

缺省参数声明顺序[Swift]

正是因为Swift语法中没有”默认参数之后不能再出现无默认值的参数”这样的规则,上面方法的声明在
Swift 里都是合法可用的.

当然针对Swift中值得一提的是,默认参数都是需要外部标签的,如果没有指定外部标签,那么
Swift
会默认自动加上同名的标签,也就相当于在参数声明前加上井号#。实际上我们声明的sayHello和sayHello1的符号是这样的:

外部标签[Swift]

外部标签名意义在于,不像其他普通方法调用那样省掉第一个参数标签名,这是为了不产生参数命名上的歧义.

关于缺省参数演示的代码在这里[DefaultArgumengSample
github]

在实例方法中修改值类型

结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择
变异
这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的
self 属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。

struct Point { var x = 0.0, y = 0.0mutating func moveByX(deltaX: Double, y deltaY: Double) {x += deltaXy += deltaY }}var somePoint = Point(x: 1.0, y:1.0)somePoint.moveByX(2.0, y: 3.0)print("The point is now at (\(somePoint.x), \(somePoint.y))")// 打印输出: "The point is now at "

注意:不能在结构体类型常量上调用可变方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行;

三、函数类型

     
 函数是一种特殊的数据类型,每一个函数属于一种数据类型,示例如下:

ca88会员入口 14

图例10

       
函数也可以作为参数传入另一个函数,这十分类似于Objective-C中的block语法,示例如下:

ca88会员入口 15

图例11

     
Swift一个函数也可以作为另一个函数的返回值,同时支持函数的嵌套示例如下:

ca88会员入口 16

图例12

类型方法

实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做类型方法。声明结构体和枚举的类型方法,在方法的
func 关键字之前加上关键字 static 。类可能会用关键字 class
来允许子类重写父类的实现方法。

下标脚本
可以定义在类、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问集合(collection),列表或序列(sequence的快捷方式,使用下标脚本的索引设置和获取值,不需要再调用实例的特定的赋值和访问方法。

格式

subscript(index: Int) -> Int { get { // 返回与入参匹配的Int类型的值 } set { // 执行赋值操作 }}

newValue
的类型必须和下标脚本定义的返回类型相同。与计算型属性相同的是set的入参声明
newValue 就算不写,在set代码块中依然可以使用默认的 newValue
这个变量来访问新赋的值。

下标脚本的使用场景:根据使用场景不同下标脚本也具有不同的含义。通常下标脚本是用来访问集合(collection),列表或序列中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现下标脚本来提供合适的功能。

断言是一种实时监测条件是否为 true 的方法使用场景:

1.监测数组的索引,不能太小或者太大,否则造成数组越界2.监测传递给函数的参数,如果是无效参数,将不能够在函数中使用

let age = -3assert(age >= 0, "A person's age cannot be less than zero")

使用注意:

断言会导致程序运行的终止,如果不管条件是否成立,都要继续往下执行代码,那就不要使用断言,断言可以保证程序在开发过程中被及时发现,但在发布的时候最好不用;

属性分为存储属性,计算属性以及类型属性;

  • 存储属性:1.就是存储在对象中的一个变量或者常量.(类似于其他面向对象语言中的成员变量);2.延迟存储属性:当第一次使用时才进行初始化属性(使用
    lazy
    修饰),(必须是变量不能是常量);3.可以给存储属性提供一个默认值,也可以在初始化方法中对其进行初始化
  • 计算属性:1.不是直接存储值,而是提供getter 和
    setter方法的;2.存储属性一般只提供getter方法3.如果只提供getter,而不提供setter,则该计算属性为只读属性,并且可以省略get{}
  • 类属性:1.直接使用类型名访问属性,
    类属性使用static来修饰,类属性只能是计算属性

class Student : NSObject { // 定义属性 // 存储属性 var age : Int = 0 var name : String? var chineseScore : Double = 0.0 var mathScore : Double = 0.0 // 计算属性 var averageScore : Double { get { return (chineseScore + mathScore) / 2 } // 没有意义.newValue是系统分配的变量名,内部存储着新值 set { self.averageScore = newValue } } // 类属性 static var corseCount : Int = 0}// 设置类属性的值Student.corseCount = 3// 取出类属性的值print(Student.corseCount)

监听属性的改变

  • 在OC中我们可以重写set方法来监听属性的改变
  • Swift中可以通过属性观察者来监听和响应属性值的变化通常是监听存储属性和类属性的改变.(对于计算属性,我们不需要定义属性观察者,因为我们可以在计算属性的setter中直接观察并响应这种值的变化)
  • 我们通过设置以下观察方法来定义观察者willSet:在属性值被存储之前设置。此时新属性值作为一个常量参数被传入。该参数名默认为newValue,我们可以自己定义该参数名didSet:在新属性值被存储后立即调用。与willSet相同,此时传入的是属性的旧值,默认参数名为oldValuewillSet与didSet只有在属性第一次被设置时才会调用,在初始化时,不会去调用这些监听方法同时willSet,
    didSet和 set,get 不能共存

class Person : NSObject { var name : String? { // 可以给newValue自定义名称 willSet { // 属性即将改变,还未改变时会调用的方法 // 在该方法中有一个默认的系统属性newValue,用于存储新值 print print } // 可以给oldValue自定义名称 didSet  { // 属性值已经改变了,会调用的方法 // 在该方法中有一个默认的系统属性oldValue,用于存储旧值 print print } } var age : Int = 0 var height : Double = 0.0}

子类可以为继承来的实例方法(instance method),类方法(class
method),实例属性(instance
property),或下标脚本(subscript)提供自己定制的实现(implementation)。我们把这种行为叫重写(overriding)。如果要重写某个特性,你需要在重写定义的前面加上
关键字。重写属性的细节:1.无论继承来的是存储属性还是计算属性,都可以进行重写;2.可以提供
getter,setter,willSet,didSet
进行属性重写;3.可以将一个继承来的只读属性重写为一个读写属性;4.不可以将一个继承来的读写属性重写为一个只读属性;

注意:重写存储属性需要提供 set 和 get 方法; final
修饰的属性,方法,下表脚本都不能被子类重写,被 final
秀恩是的类不能被继承;

构造函数类似于OC中的初始化方法:init方法默认情况下载创建一个类时,必然会调用一个构造函数即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数。如果是继承自NSObject,可以对父类的构造函数进行重写

class Person: NSObject { var name : String var age : Int //重写了NSObject的构造方法 override init() { name = "John" age = 0 }}// 创建一个Person对象let p = Person()

自定义构造函数

class Person: NSObject { var name : String var age : Int //注意:如果自定义了构造函数,会覆盖init()方法.即不在有默认的构造函数 init(name : String, age : Int) { self.name = name self.age = age }}// 创建一个Person对象let p = Person(name: "John", age: 18)

字典转模型真实创建对象时,更多的是将字典转成模型

注意:

  • 去字典中取出的是NSObject,任意类型.
  • 可以通过as!转成需要的类型,再赋值

class Person: NSObject { var name : String var age : Int // 自定义构造函数,会覆盖init()函数 init(dict : [String : NSObject]) { name = dict["name"] as! String age = dict["age"] as! Int }}// 创建一个Person对象let dict = ["name" : "John", "age" : 18]let p = Person(dict: dict)

字典转模型利用KVC字典转模型会更加方便

注意:

  • KVC并不能保证会给所有的属性赋值
  • 因此属性需要有默认值
  • 基本数据类型默认值设置为0
  • 对象或者结构体类型定义为可选类型即可(可选类型没有赋值前为nil)

class Person: NSObject { // 结构体或者类的类型,必须是可选类型.因为不能保证一定会赋值 var name : String? // 基本数据类型不能是可选类型,否则KVC无法转化 var age : Int = 0 // 自定义构造函数,会覆盖init()函数 init(dict : [String : NSObject]) { // 必须先初始化对象 super.init() // 调用对象的KVC方法字典转模型 setValuesForKeysWithDictionary }}// 创建一个Person对象let dict = ["name" : "John", "age" : 18]let p = Person(dict: dict)

闭包和OC中的block非常相似OC中的block是匿名的函数Swift中的闭包是一个特殊的函数block和闭包都经常用于回调在函数中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:•
全局函数是一个有名字但不会捕获任何值的闭包•
嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包•
闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包

block写法:

类型: 返回值(^block的名称) 值: ^ { // 执行的代码 };

闭包写法:

类型:-> 技巧:初学者定义闭包类型,直接写再填充参数和返回值 值: {  -> 返回值类型 in // 执行代码 }

闭包的简写

  • 如果闭包没有参数,没有返回值.in和in之前的内容可以省略

尾随闭包写法:

  • 如果闭包是函数的最后一个参数,则可以将闭包写早()后面如果函数只有一个参数,并且这个参数是闭包,那么()可以不写

func someFunctionThatTakesAClosure(closure: () -> Void) {// 函数体部分}// 以下是不使用尾随闭包进行函数调用someFunctionThatTakesAClosure({// 闭包主体部分})// 以下是使用尾随闭包进行函数调用someFunctionThatTakesAClosure() {// 闭包主体部分}

闭包的循环引用问题

//weakSelf?.view//如果前面的可选类型没有值,后面所有的代码都不会执行//如果前面的可选类型有值,系统会自动将weakSelf进行解包,并且使用weakSelfweak vaar weakSelf = self;request.loadData{ (jsonData ) -> () weakSelf?.view.backgroundColor = UIColor.redColor()}//推荐写法weak vaar weakSelf = self;request.loadData{ [weak self] (jsonData ) -> () self?.view.backgroundColor = UIColor.redColor()}//unowned不是太安全相当于 OC 中的__unsafe_unretainedweak vaar weakSelf = self;request.loadData{ [unowned self] (jsonData ) -> () self.view.backgroundColor = UIColor.redColor()}

swift中也有懒加载的方式(苹果的设计思想:希望所有的对象在使用时才真正加载到内存中)和OC不同的是swift有专门的关键字来实现懒加载lazy关键字可以用于定义某一个属性懒加载

 // 懒加载的本质是,在第一次使用的时候执行闭包,将闭包的返回值赋值给属性 // lazy的作用是只会赋值一次;也是闭包的一种书写形式 lazy var array : [String] = { () -> [String] in return ["why", "lmj", "lnj"] }()

四、从一个系统函数看闭包

       
Swift标准函数库中提供了一个sort排序函数,对于已经元素类型的数组,调用sort函数会进行重新排序并返回新的排序后的数组。这个sort函数可以接收一个返回值为Bool类型的闭包,来确定第一个元素是否排在第二个元素前面。代码示例如下:

ca88会员入口 17

图例13

       
Swift语言有一个很显著的特点就是简洁,可以通过上下文推断出类型的情况一般开发都可以将类型的书写省略,这也是Swift语言设计的一个思路,由于闭包是作为函数的参数传入函数中的,因为函数参数的类型是确定,因此闭包的类型是可以被编译器推断出来的,开发者也可以将闭包的参数类型和返回值省略,上面的代码可以简写如下:

ca88会员入口 18

图例14

       
实际上,如果闭包中的函数体只有一行代码,可以将return关键字也省略,这时会隐式的返回此行代码的值,如下:

ca88会员入口 19

图例15

       
看到上面的表达式,是不是有点小震惊,闭包表达式竟然可以简写成这样!然而,你还是小看的Swift开发团队,后面的语法规则会让你明白什么是简洁的极致。可以看到上面的代码实现还是有3部分:参数和返回值,闭包关键字,函数体。参数和返回值即是参数列表,p1,p2,虽然省略了参数类型和返回值类型,但这部分的模块还在,闭包关键字即是in,它用来表示下面将是闭包的函数体,p1>p2即是函数体,只是这里省略了return关键字。闭包中既然参数类型和返回值类型编译器都可以自己推断出来,那么参数的数量编辑器也是可以自行推断的,因此,参数列表实际上也是多余的,闭包中会自动生成一些参数名称,和实际的参数数量向对应,例如上面sort函数中的闭包有两个参数,系统会自动生成$0和$1这两个参数名,开发者可以直接使用,因为参数列表都会省略了,那么也不再需要闭包关键字in来分隔参数列表与函数体,这时,闭包的写法实际上变成了如下的模样:

ca88会员入口 20

图例16

     
你没有看错,加上左右的大括号,一共7个字符,完成了一个排序算法。除了Swift,我不知道是否还有第二种语言可以做到。抛开闭包不说,Swift中还有一种语法,其可以定义类型的运算符方法,例如String类型可以通过=,<,>来进行比较,实际上是String类中实现了这些运算符方法,在某种意义上说,一个运算符即类似与一个函数,那么好了,sort函数中需要传入的方法对于某些类型来说实际上只是需要一个运算符,示例如下:

ca88会员入口 21

图例17

这次你可以真的震惊了,完成排序新算法只需要一个字符,不折不扣的一个字符。

五、Swift中闭包的更多特点

       
 Swift中的闭包还有一个有趣的特点,首先闭包是作为参数传入另一个函数中的,因此常规的写法是将闭包的大括号写在函数的参数列表小括号中,如果闭包中的代码很多,这时在代码结构上来看会变得并不太清晰,为了解决这个问题,Swift中这样规定:如果这个闭包参数是函数的最后一个参数,开发者可以将其拉出小括号,在函数尾部实现闭包代码,示例如下:

ca88会员入口 22

图例18

     
 如果一个函数中只有一个参数,且这个参数是一个闭包,那么开发者使用闭包结尾这种写法,完全可以将函数的参数列表小括号也省略掉,示例如下:

ca88会员入口 23

图例19

       
Swift中还有一个闭包逃逸的概念,这个很好理解,当闭包作为参数传递进函数时,如果这个闭包只在函数中被使用,则开发者可以将这个闭包声明成非逃逸的,即告诉系统当此函数结束后,这个闭包的声明周期也将结束,这样做的好处是可以提高代码性能,将闭包声明称非逃逸的类型使用@noescape关键字,示例如下:

ca88会员入口 24

图例20

逃逸的闭包常用于异步的操作,例如这个闭包是异步处理一个网络请求,只有当请求结束后,闭包的声明周期才结束。非逃逸的闭包还有一个有趣的特点,在其内部如果需要使用self这个关键字,self可以被省略。

闭包也可以被自动的生成,这种闭包被称为自动闭包,自动闭包可以自动将表达式封装成闭包,开发者不需要再写闭包的大括号格式,自动闭包不接收参数,返回值为其中表达式的值。示例如下:

ca88会员入口 25

图例21

自动闭包默认是非逃逸的,如果要使用逃逸的闭包,需要手动声明,如下:

ca88会员入口 26

图例22

相关文章