一、swift新元素
Tip1:柯里化 将方法进行柯里化,把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数,返回结果的新方法。
func addTwoNumbers(a: Int)(num: Int) -> Int {
return a + num
}
let addToFour = addTwoNumbers(4)
let result = addToFour(num: 6)
func greaterThan(comparor: Int)(input: Int) -> Bool {
return input > comparor
}
let greaterThan10 = greaterThan(10);
greaterThan10(input: 13)
greaterThan10(input: 9)
将selector进行柯里化:
protocol TargetAction {
func performAction()
}
struct TargetActionWrapper<T: AnyObject>: TargetAction {
weak var target: T?
let action: (T)->()->()
func performAction() -> () {
if let t = target{
action(t)()
}
}
}
enum ControlEvent {
case TouchUpInside
case ValueChanged
}
class Control{
var actions = [ControlEvent: TargetAction]()
func setTarget<T: AnyObject>(target: T, action: (T)-> ()->(),controlEvent:ControlEvent) {
actions[controlEvent] = TargetActionWrapper(target: target, action: action)
}
func removeTargetForControlEvent(controlEvent: ControlEvent){
actions[controlEvent] = nil
}
func performActionForControlEvent(controlEvent: ControlEvent){
actions[controlEvent]?.performAction()
}
}
Tip2 将protocol的方法声明为mutating
mutating为了能在这个方法修改struct或者enum的变量,如果没有 在接口里写mutating的话,别人如果用struct或者enum来实现这接口就不能在方法里改变自己的变量
protocol Vehicle {
var numberOfWheels: Int {get}
var color: UIColor {get set}
mutating func changeColor()
}
struct MyCar: Vehicle {
let numberOfWheels = 4
var color = UIColor.blueColor()
mutating func changeColor() {
color = UIColor.redColor()
}
}
如果把mutating去掉,会报错说mycar过不了编译,不能改变结构体成员,添加后,会说没有实现Vehicle 这个接口
Tip3 sequence
/*
先定义一个实现了GeneratorType protocol 的类型,
GeneratorType需要指定一个 typealias Element
以及提供返回一个Element?的方法 next()
*/
class ReverseGenerator: GeneratorType {
typealias Element = Int
var counter: Element
init<T>(array: [T]){
self.counter = array.count - 1
}
init(start: Int){
self.counter = start
}
func next() -> Element? {
return self.counter < 0 ? nil : counter--
}
}
/*
然后定义一个实现了sequenceType
需要指定一个 typealias Generator
以及提供返回一个Generator?的方法 generate()
*/
struct ReverseSequence<T>: SequenceType {
var array: [T]
init(array: [T]){
self.array = array
}
typealias Generator = ReverseGenerator
func generate() -> Generator {
return ReverseGenerator(array: array)
}
}
let arr = [0,1,2,3,4];
for i in ReverseSequence(array: arr){
print("index \(i) is \(arr[i])")
}
Tip4 多元组Tuple
OC的时候是:
func swapMe<T>(inout a: T, inout b: T){
let temp = a
a = b
b = temp
}
引入多元组的概念后就更简单了:
func swapMe<T>(inout a: T, inout b: T){
(a,b) = (b,a)
}
还有一个常用的地方错误的处理,OC时代习惯在需要错误处理时候先做一个NSError的指针,然后把地址传到方法里等待填充。
如:NSError *error = nil;
Bool success = [[NSFileManager defaultManager]moveItemAtPath:@"/path/to/target" toPath:@"/path/to/destination" error:&error];
if(!success){
NSLog(@"%@",error);
}
引入多元组之后,这样写:
func doSomethingMightCauseError() ->(Bool,NSError?){
//。。。 做某些操作,成功结果放在succe中
if success {
return (true,nil)
}else{
return(false,NSError(domain: "SomeErrorDomain",code: 1,userInfo: nil))
}
}
//调用函数
let (success,maybeError) = doSomethingMightCauseError()
if let error = maybeError{
//发生了错误
}
Tip 5 @autoclosure 和??操作符
@autoclosure做的事情就是把一句表达式自动封装包成一个闭包
func logIfTrue(predicate: () ->Bool){
if predicate() {
print("True")
}
}
logIfTrue{2>1}
类似上面的例子,我们牙根看不明白是闭包,和函数很像,这时候@autoclosure就派上用场了。
func logIfTrue(@autoclosure predicate: () ->Bool){
if predicate() {
print("True")
}
}
logIfTrue(2>1)
最后一句提醒:@autoclosure并不支持带有输入参数的写法,也就是说只有形如()->T的参数才能使用这个特性进行简化。
Tip6 optional Chaning
使用optional Chaning可以省去很多不必要的判断和取值步骤。
class Toy {
let name : String
init(name: String){
self.name = name
}
}
class Pet{
var toy: Toy?
}
class Child{
var pet: Pet?
}
extension Toy{
func play(){
}
}
let playClosure = {(child:Child)->()?in child.pet?.toy?.play()}
if let result: () = playClosure(xiaoming){
print("好开心")
}else{
print("没有玩具可以玩")
}
Tip7操作符
swift支持重载操作符。
1定义Vector2D相加的操作
//struct Vector2D {
//
// var x = 0.0
// var y = 0.0
//}
//
//let v1 = Vector2D(x: 2.0,y: 3.0)
//let v2 = Vector2D(x: 1.0,y: 4.0)
//func +(left: Vector2D,right: Vector2D) -> Vector2D {
// return Vector2D(x: left.x+right.x,y: left.y+right.y)
//}
//
//let v3 = v1+v2
2 定义像。
struct Vector2D {
var x = 0.0
var y = 0.0
}
infix operator +*
{
associativity none //定义了结合规律,即多个同类的操作符出现时候-计算顺序
precedence 160 //precedence表示优先级
}
let v1 = Vector2D(x: 2.0,y: 3.0)
let v2 = Vector2D(x: 1.0,y: 4.0)
func +*(left: Vector2D,right: Vector2D) -> Double {
return left.x * right.x + left.y * right.y
}
let v3 = v1 +* v2
Tip8 func的参数修饰
//声明一个swift方法,一般不去指定参数前面的修饰符,而是直接声明参数,如:
func incrementor(variable: Int) -> Int {
return variable + 1
}
a 有指定修饰符的时候,
func incrementor(var variable: Int) -> Int {
return ++variable
}
var luckyNumber = 7
let newNumber = incrementor(luckyNumber)
print(luckyNumber) //7
b 方法内部直接修改输入的值,使用关键字inout来对参数进行修饰。
func incrementor(inout variable: Int){
variable += 1
}
var luckyNumber = 7
incrementor(&luckyNumber)
print(luckyNumber) //8
c
//注意参数的修饰具有传递的限制,对于跨越层级的调用,我们跨越保证同一参数的修饰是统一的
func makeIncrementor(addNumber: Int) -> ((inout Int)->()) {
func incrementor(inout variable: Int)->(){
variable += addNumber
}
return incrementor;
}
Tip9 方法参数名称省略
-(Bool)writeToFile:(NSString *)path
atomically:(BOOL)useAuxiliaryFile
encoding:(NSStringEncoding)enc
error:(NSError **)error
在类型的init方法中是需要加入名称标签的,如:
class Car{
init(color: UIColor,weight: Int){
//...
}
}
let car = Car(color: UIColor.redColor(),weight: 10)
class Car{
init(color: UIColor,weight: Int){
//...
}
}
let car = Car(color: UIColor.redColor(),weight: 10)
//对于实例方法,我们调用的时候swift将忽略第一个参数的名称标签,强制要求之后的参数名称。
extension Car{
func moveToX(x: Int,y: Int){
//...
}
}
car.moveToX(10, y: 20)
对于类方法也是如此:
extension Car {
class func findACar(name: String,color:UIColor)->Car? {
var result: Car?
//...
return result
}
}
let myPorsche = Car.findACar("Porsche", color: UIColor.yellowColor())
有一个例外:方法若s全局方法 ,参数名称可以省略。
func finACar(name:String,color:UIColor) -> Car? {
var result: Car?
//...
return result
}
let myFerrari = finACar("Ferrari", UIColor.redColor())
Tip10 字面量转换
let aNumber = 3
let aString = "Hello"
let aBool = true
其中3,Hello,true是字面量。
例子:
我们有一个Person类,通过string赋值生成person对象:
class Person: StringLiteralConvertible {
let name: String
init(name value: String){
self.name = value
}
required convenience init(stringLiteral value:String){
self.init(name: value)
}
required convenience init(extendedGraphemeClusterLiteral value: String) {
self.init(name: value)
}
required convenience init(unicodeScalarLiteral value:String) {
self.init(name: value)
}
}
let p:Person = "xiaoming"
print(p.name)
Tip11 下标
Tip12 方法嵌套
func appendQuery(var url: String,
key: String,
value: AnyObject) ->String{
func appendQueryDictionary(var url: String,
key: String,
value:[String: AnyObject]) -> String {
//..
return result
}
func appendQueryArray( var url: String,
key: String,
value:[AnyObject]) -> String {
//..
return result
}
func appendQuerySingle(var url: String,
key: String,
value:AnyObject) -> String {
//..
return result
}
if let dictionary = value as? [String: AnyObject]{
return appendQueryDictionary(url,key: key,value: dictionary)
}else if let array = value as?[AnyObject]{
return appendQueryArray(url, key: key, value: array)
}else {
return appendQuerySingle(url, key: key, value: value)
}
}
Tip13 命名空间
//myFramework.swift
public class MyClass{
public class func hello(){
print("hello from framework")
}
}
//myApp.swift
class myClass{
class func hello() {
print("hello from app")
}
}
//在使用的时候,可能出现冲突的时候,这样写:
myClass.hello()
myFramework.myClass.hello()
或者使用类型嵌套也行:
struct myClassContainer1{
class myClass{
class func hello() {
print("hello from app")
}
}
}
struct myClassContainer2{
class myClass{
class func hello() {
print("hello from myFramework")
}
}
}
调用的时候:
myClassContainer1.myClass.hello()
myClassContainer2.myClass.hello()
Tip14 Any和AnyObject
AnyObject可以代表任何class类型的实例,代替OC的id,进行参数传递和方法返回,只适用除了array和dictionary以外的class类
Any可以表示任意类型,甚至包括方法类型,刚好除了class以外,还可以表示struct和enum在内的所有类型
都是妥协的产物,如果在自己的代码使用这两者,往往意味着代码可能存在在结构和设计上的问题,应该及时重新审视。简单来说,我们最好避免使用甚至依赖两者,而去尝试明确地指出确定的类型。
Tip15 typealias和泛型接口
typealias是用来为已经存在的类型重新定义名字的,通过命名,可以使代码变得更加清晰。
泛型接口:
protocol GeneratorType{
typealias Element
mutating func next()->Element?
}
Tip16 可变参数函数
指:可以接受任意多个参数的函数。
只需要在声明参数的时候在类型后面加上...就可以。
func sum(input: Int...)->Int{
}
Tip17初始化方法顺序
class Cat{
var name: String
init(){
name = "cat"
}
}
class Tiger: Cat {
let power: Int
override init() {
power = 10
super.init()
name = "tiger"
}
}
// 1设置子类自己需要初始化的参数
//2 调用父类的相应的初始化方法,super.init()
//3 对父类的需要改变成员进行设定
Tip18 Designated,Convenience和Required
初始化方法遵守原则是:
1.初始化路径必须保证对象完全初始化,这可以通过调用本类型的designated初始化方法来得到保证
2. 子类 的designated初始化方法必须调用父类的designated方法,以保证父类也完成初始化。
Tip19初始化返回nil
可能返回nil的init方法都加上了?标记,如:
convenience init?(string URLString: String)
Tip20 protocol组合
typealias PetLike = protocol<KittenLike,DogLike>
Tip 21static和class
static 有存储属性,class没有。
记住:除了确实是在具体的class中实现以外,其他情况均使用static就行了。
Tip22 多类型和容器
swift中有两个原生的容器类型,array和dictionary
都是泛型,就是说我们在一个集合中只能放同一个类型的元素。
Tip23default参数
Tip24正则表达式
Tip25模式匹配
func ~=<T: Equatable>(a: T,b: T)->Bool
func ~=<T>(lhs: _optionalNilComparisonType,rhs:T?)->Bool
func ~=<I: IntervalType>(pattern: I,value: I.Bound)->Bool
switch在swift中非常强大。
Tip26...和..<
全闭合的范围操作和半闭合范围操作。
Tip27 AnyClass,元类型和.self
AnyClass任意类型本身。
.Type是某个类型的元类型。
typealias AnyClass = AnyObject.Type //通过AnyObject.Type 方式获得一个元类型
//也就是说我们可以声明一个元类型来存储A这个类型本身,而从A中取出其类型时,我们需要使用到.self
class A {
}
let typeA:A.Type = A.self
注意:可以强制的让A类型取值是一个AnyClass,如:
class A {
}
let typeA:AnyClass = A.self
Tip28接口和类方法中的Self
Tip29 动态类型和多方法
Tip29 动态类型和多方法
Tip 30 属性观察
willSet和didSet
newValue和oldValue
31 final
final在class,func,和var前面进行修饰,表示不允许对该内容进行继承或者重写操作。
Tip 31 finalTip 32 lazy修饰符和lazy方法
延时加载和延时初始化。
Tip33 find
寻找数组中某个元素
Tip34 Reflection和MirrorType
Tip35 隐式解包Optional
在类型后加上一个感叹号!告诉编译器需要一个隐式解包的optional值。
Tip36多重的optional
Tip37Optional Map
二、从OC到Swift
Tip 38 Selector
@selector是OC时代的关键字,将一个方法转换并赋值给一个SEL类型,类似一个动态的函数指针。其实是OCruntime的概念。
设定target-action,到询问是否响应某个方法,再到指定接受通知时需要调用的方法等等。
swift中没有@selector,要生成selector只能使用字符串,对应原来SEL的类型是一个叫做Selector的结构体,提供接受字符串初始化方法。
-(void)callMe{}
func callMe(){}
注意:如果是swift中的private方法,在调用这个selector时会遇到unrecognized selctor错误,只要在private 前面加上@objc关键字就可以找到相应的方法了。
Tip39实例方法是调用
swift中有一类很有意思的写法,不直接使用实例来调用这个实例方法,而是通过类型取出这个类型中某个实例方法的签名,然后通过传递实例拿到实际需要调用的方法。比如:
class MyClass{
func method(number: Int) -> Int {
return number + 1
}
}
//let object = MyClass()
//let result = object.method(1)或者
let f = MyClass.method
let object = MyClass()
let result = f(object)(1)
Tip40单例
swift:
class MyManager{
private static let sharedInstance = MyManager()
class var sharedManager: MyManager {
return sharedInstance
}
}
OC:
@implementation MyManager
+(id)sharedManager{
static MyManager *staticInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(@onceToken,^{
staticInstance = [[self alloc]init];
});
return staticInstance;
}
@end
Tip41 条件编译
OC中可以使用#if或者#ifdef编译条件分支控制某些代码需要编译,哪些代码不需要编译。
swift中没有宏定义的概念,首先#if这一套编译标记还是存在的,使用语法也和原来没有区别:
#if<condition>
#elseif<condition>
#else
#endif
例子:
#if os(OSX)
typealias Color = NSColor
#else
typealias Color = UIColor
#endif
Tip42 编译标记
OC插入#param符号来标记代码的区间,为了方便找代码。浏览代码
#warning提示警告
swift中是使用//MARK:这样的标记,后面加上名称
//TODO:和//:FIXME:,本身也会显示出来。
swift中暂时没有在编译时像#warning那样生成警告的方法
Tip43 @UIApplicationMain
swift 的app也是需要main函数的,只不过默认情况下是@UIApplicationMain帮助我们自己自动生成而已。
比如我们删除@UIApplicationMain后,在项目中添加一个main.swift文件,然后加上这样的代码:
UIApplicationMain(Process.argc,Process.unsafeArgv,nil,NSStringFromClass(AppDelegate))
Tip44@objc和dynamic
swift考虑和OC兼容。
在swift中添加{product-module-name}-Bridging-Header.h文件就可以使用OC代码
使用OC代码或者特性调用纯swift的类型时,需要加上@objc来修饰,添加@objc修饰符并不意味着这个方法或者属性会变成动态派发,swift依然可能会将其化为静态调用。如果你需要和OC里动态的调用时相同的运行时特性需要加上修饰符dynamic.
Tip 可选接口
原生的swift protocol没有可选项,所有定义的方法必须实现。
如果想和OC那样定义可选方法,需要在protocol定义前加上@objc,
和OC的@oprional不同,swift使用没有@符号的关键字optional来定义可选方法:
@objc protocol OptionalProtocol{
optional func optionalMethod()//可选
func necessaryMethod()//必须
optional func anotherOptionalMethod() //可选
}
注意:对于struct和enum类型,无法令它们实现的接口含有可选方法或者属性。
Tip46 内存管理,weak和unowned
swift也是自动管理内存,通过初始化创建一个对象,会自动管理和分配内存,释放过程使用ARC原则,一个对象不再引用会自动回收。我们只需要保证在合适的时间将引用置空比如超出作用于或者手动设为nil等,就可以确保内存使用不出现问题。
但还是有个限制:循环引用。
如果能够确定在访问时不会已经被释放就尽量使用unowned;如果存在被释放的可能就选择weak
Tip47@autoreleasepool
Tip48值类型和引用类型
swift分为两种类型:值类型(struct和enum定义的)和引用类型(class定义的)
Tip49Foundation框架
Tip50String还是NSString
尽量使用string,而不是nsstring,转换麻烦。
Tip51UnsafePoint
UnsafePointer专门针对指针的转换。
对于C中的基础类型:
int - CInt
bool-CBool
Tip52C指针的内存管理
需要添加关键字detroy和dealloc.
Tip53 COpaquePointer和CFunctionPointer
Tip54GCD和延时调用
dispatch_afeter延时调用
Tip55获取对象类型
动态时候的:
let date = NSDate()
let name = date.dynamicType
print(name)
Tip56自省
isKindOfClass
isMemberOfClass
Tip57类型转换
as!关键字做强制类型转换
for object in self.view.subviews{
if let view = object as? UIView{
view.backgrundColor = UIColor.redColor()
}
}
Tip58KVO
1需要属性dynamic修饰
2对于那些非NSObject的swift类型怎么办?
swift没有通过KVC实现,更不用谈“对属性进行KVO”了、。语言中还没有原生的类似KVO的观察机制。
Tip59局部scope
OC利用大括号实现代码隔离;
swift使用scope的形式:
func local(closure: ()->()){
closure()
}
OC还有一个很棒的技巧是使用GNU C的声明扩展来限制局部作用于的时候同时进行赋值。如:
self.titleLabel = ({
UILabel *label = [UILabel alloc]initWithFrame:CGRectMake(150,30,20,40)];
........
[view addSubview:label];
label;
});
swift没有GNU C的扩展,使用匿名闭包就可以实现:
titleLabel = {
let label = UILabel(frame:CGRectMake(150,30,20,40))
...
self.view.addSubview(label)
return label
}()
Tip60判等
OC:isEqualToString:;或者isEqual,OC中==是判断两个对象是否指向同一块内存地址。
swift :==操作符来判断,!=
let str1 = "快乐的字符串"
let str2 = "快乐的字符串"
str1 == str2
Tip61 哈希(哈希表或者散列表)
OC的-hash方法
swift的Hashable的接口
Tip62 类簇
类簇:使用一个统一的公共的类来订制单一的接口。如NSNumber。
Tip63Swizzle
通过swizzle在运行时对某些方法的实现进行替换。
Tip64调用C动态库
通过OC来调用
Tip65输出格式化
printIn,支持字符串插值。
Tip66Options
Option值被映射为满足RawOptionSetType接口的struct类型,以及一组静态的get属性。
OC的NS_OPTIONS定义按位错开的。
Tip67性能考虑
相对于OC,swift最大的改变就在于方法调用上的优化。
Tip68数组enumerate
EnumerateGenerator和enumerateObjectsUsingBlock:
Tip69 类型编码@encode
Tip70C代码调用和@asmname
Tip71sizeOf和sizeofValue
Tip72delegate
Tip73Associated Object
Tip74 Lock
TIP 75Toll-Free Briding 和 Unmanaged
在swift中,CF对象也在ARC的管辖范围了。
三、swift与开放环境及一些实践
Tip76swift命令行工具
Tip77随机数生成
Tip78Printable和DebugPrintable
Tip79错误处理
Tip80断言
Tip81fatalError
Tip82代码组织和Framework
Tip83playground延时运行
Tip84playground可视化
Tip85与项目协作
Tip86playground限制
Tip87数学和数字
Tip88JSON
Tip89NSNull
Tip90文档注释
Tip91Log输出
Tip92溢出
Tip93宏定义define
swift没有宏定义
Tip94属性访问控制
Tip95swift中的测试
Tip96CoreData
Tip97闭包歧义
Tip98泛型扩展
Tip99兼容性
Tip100列举enum类型