swift 学习笔记
Swift2.0
1.defer译为延缓、推迟之意类似栈
注意作用域,其次是调用顺序——即一个作用域结束(注意),该作用域中的defer语句自下而上调用(相同作用域下后入先执行)。
不是函数结束时开始执行defer栈推出操作,而是每当一个作用域结束就进行该作用域defer执行
2.guard有控制、警戒之意切记else中一定需要有返回的语句,比如return、continue、break、throw这种提早退出的关键字!!
guard age >= 13 else{
return
}
if age >= 13 else{
return
}
*/
//MARK:/******************************读王巍*******************************/
/*
1.柯里化Swift里可以将方法进行柯里化(Currying)14,也就是把接受多个参数的方法变换成接受第一个参数的方法,返回接受余下的参数并且返回结果的新方法。举个例子,Swift中我们可以这样写出多个括号的方法
func addTwoNumbers(a: Int)(num: Int) -> Int {
return a + num
}
let addToFour = addTwoNumbers(4) // addToFour是一个Int -> Int方法
let result = addToFour(num: 6) // result = 10
2.Sequence(序列化)
Swift的for...in可以用在所有实现了SequenceType的类型上,而为了实现SequenceType你首先需要实现一个GeneratorType(发动机类型)
class ReverseGenerator: GeneratorType {
typealias Element = Int
var counter: Element
init(array: [T]) {
self.counter = array.count - 1
}
init(start: Int) {
self.counter = start
}
func next() -> Element? {
return self.counter < 0 ? nil : counter--
}
}
struct ReverseSequence: SequenceType {
var array: [T]
init (array: [T]) {
self.array = array
}
typealias Generator = ReverseGenerator
func generate() -> Generator {
return ReverseGenerator(array: array)
}
}
Sequence
let arr = [0,1,2,3,4]
//对SequenceType可以使用for...in来循环访问
for i in ReverseSequence(array: arr) {
print("Index \(i) is \(arr[i])")
}
3.@autoclosure:把一句表达式自动地封装成一个闭包(closure)作用是使其清晰
注意:@autoclosure并不支持带有输入参数的写法,也就是说只有形如() -> T的参数才能使用这个特性进行简化
func logIfTrue(predicate: () -> Bool) {}
func logIfTrue(@autoclosure predicate: () -> Bool) {}
??:var currentLevel = level ?? startLevel level是否为nil是取右边否则取左边
4.字面量转换(通过结构体基本数据类型生成对象)
所谓字面量,就是指像特定的数字,字符串或者是布尔值这样,能够直接了当地指出自己的类型并为变量进行赋值的值
例如Person类String赋值来生成Person对象的话
Person类实现StringLiteralConvertible协议后可以直接初始化对象
let p: Person = "xiaoMing"
一.需要实现以下协议
• ArrayLiteralConvertible
• BooleanLiteralConvertible
• DictionaryLiteralConvertible
• FloatLiteralConvertible
• NilLiteralConvertible
• IntegerLiteralConvertible
• StringLiteralConvertible
class Person: StringLiteralConvertible {
//typealias StringLiteralType
typealias StringLiteralType = String
let name: StringLiteralType
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)
}
}
func testPerson(){
let p : Person = Person(stringLiteral: "zzq")
print(p.name)
//直接通过string可以初始化对象
let p2 :Person = "ccc"
print(p2.name)
}
//在Swift中自定义下标(subscript)可以给类结构体(基本数据是结构体)定义下标[]
extension Int {
//定义下标是Int类型
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 1...digitIndex {
decimalBase *= 10
}
return self * decimalBase * 2
}
}
class DailyMeal{
enum MealTime{
case Breakfast
case Lunch
case Dinner
}
var meals: [MealTime : String] = [:]
//定义下标是MealTime类型
subscript(requestedMeal : MealTime) -> String{
get{
if let thisMeal = meals[requestedMeal]{
return thisMeal
}else{
return "Ramen"
}
}
//只读下标下面则省略神州省略get
set(newMealName){
meals[requestedMeal] = newMealName
}
}
}
func testSubscript(){
let a:Int = 123
print("i love \(a),\(a[5])")
var monday = DailyMeal()
monday[.Lunch] = "Pizza"
print(monday[.Lunch])//Output:"Pizza"
print(monday[.Dinner])//Output:"Ramen"
}
5.实例方法的动态调用
class MyClass {
func method(number: Int) -> Int {
return number + 1
}
class func come(){
print("class fun")
}
//注意!当存在和实例方法相同名的方法时候默认通过类名点方法生成的是类方法的柯里化方法所以此时加类型参数区分
class func method(number: Int) -> Int {
return number
}
//区分如下:
/*这种方式不仅在这里有效,在其他大多数名字有歧义的情况下,都能很好地解决问题
let f1 = MyClass.method // class func method的版本(由于实例和类方法相同名所以默认是类方法)
let f2: Int -> Int = MyClass.method //和f1相同(所以定义Int->Int是默认类方法)
let f3: MyClass -> Int -> Int = MyClass.method // func method的柯里化版本实例方法
*/
}
func testMyClass(){
//f:MyClass -> (Int) -> Int通过类名点方法生成一个可以"柯里化"的方法
let f = MyClass.method// MyClass.come()注意和类方法调用是不同的勿混淆
//实例化对象
let object = MyClass()
//调用方法
let result:Int = f(object)(1)
print(result)
}
6.命名空间
通过框架framework.类名调用不同的类以区分相同命名的类
通过结构体名.类名或者类名.类名区分相同命名的类
7.Any和AnyObject
AnyObject(是个协议protocol)可以代表任何class类型的实例
Any(protocol<>)可以表示任意类型,甚至包括方法(func)类型
对于OC返回id类型择对于swift返回->AnyObject?
所有的class都隐式地实现了这个接口AnyObject接口
protocol<>:需要实现空接口的接口”,其实就是任意类型的意思了。
8.typedef(OC)和typealias(swift)
有点特殊在swift中typealias对接口定义一个必须实现的别名。比如在GeneratorType和SequenceType这两个接口中,Swift都用到了这个技巧
protocol GeneratorType {
typealias Element
mutating func next() -> Element?
}
9.@UIApplicationMain
swift不象Objective-C时那样的main文件,也不存在main函数AppDelegate类的声明上方有一个@UIApplicationMain的标签
10.初始化方法顺序
与Objective-C不同,Swift的初始化方法需要保证类型的所有属性都被初始化。所以初始化方法的调用顺序就很有讲究。在某个类的子类中,初始化方法里语句的顺序并不是随意的,我们需要保证在当前子类实例的成员初始化完成后才能调用父类的初始化方法
class Cat {
var name: String
init() {
name = "cat"
}
}
class Tiger: Cat {
let power: Int
override init() {
power = 10
super.init()
name = "tiger"
}
}
一般来说,子类的初始化顺序是:
1.设置子类自己需要初始化的参数,power = 10
2.调用父类的相应的初始化方法,super.init()
3.对父类中的需要改变的成员进行设定,name = "tiger"
其中第三步是根据具体情况决定的,如果我们在子类中不需要对父类的成员做出改变的话,就不存在第3步。而在这种情况下,Swift会自动地对父类的对应init方法进行调用,也就是说,第2步的super.init()也是可以不用写的(但是实际上还是调用的,只不过是为了简便Swift帮我们完成了)。这种情况下的初始化方法看起来就很简单:
初始化方法顺序
class Cat {
var name: String
init() {
name = "cat"
}
}
class Tiger: Cat {
let power: Int
override init() {
power = 10
}
//虽然我们没有显式地对super.init()进行调用
//不过由于这是初始化的最后了,Swift替我们完成了
}
11. as?,as!,as,is四者用法
as?:返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil
as!:用以强制向下转换型,如子类实例父类引用时候该实例的向下转型,当转型失败则报错异常。
as :将某实例(实现该协议)强转为该协议调用协议的方法以此区分不同协议的同名方法
is :判断某一个对象是否是某一个特定的类(OC isKindOfClass)。
12.static和class
static一般在struct和enum中使用可以修饰方法(静态方法)存储变量(静态变量)和计算变量
class在class中可以修饰方法(类方法)计算变量,注意不能修饰存储变量
注意:1.如果在类中要使用静态变量只能使用static来修饰存储变量
2.在class被static修饰的存储变量,被class修饰计算变量,方法子类和父类都是公用的(OC也是如)
3.被class修饰的方法不能被子类重写但是OC的类方法可以被子类重写的
如果我们想在protocol里定义一个类型域上的方法或者计算属性的话,应该用哪个关键字呢?答案是使用class进行定义
13.@objc和dynamic
在swift中对于非继承NSObject类的在OC使用swift会因为OC是运行时机制{(KVC和动态派发Dynamic),在运行时决定实际调用的具体实现},而找不到所需要的这些运行时信息,所以@objc关键字将类,属性和方法暴露给OC。
注意:如果是继承NSObject ,swift则会默认自动为所有的非private的类和成员加上@objc
添加@objc修饰符并不意味着这个方法或者属性会变成动态派发,Swift依然可能会将其优化为静态调用。如果你需要和Objective-C里动态调用时相同的运行时特性的话,你需要使用的修饰符是dynamic。一般情况下在做app开发时应该用不上,但是在施展一些像动态替换方法或者运行时再决定实现这样的“黑魔法”的时候,我们就需要用到dynamic修饰符了。
//需加上@objc来使用optional可选借接口方法实现否则swift的接口都必须实现所有的接口方法
@objc protocol OptionalProtocol {
optional func optionalMethod() //可选
func necessaryMethod() //必须optional func anotherOptionalMethod() //可选
}
14.内存管理,weak和unowned
weak就是以前的weak,unowned更像以前的unsafe_unretained
class Person {
let name: String
//lazy var printName: ()->() = {
//print("The name is \(self.name)")
//}
//需改成以下[weak self]来避免闭包的循环引用
lazy var printName: ()->() = {
[weak self] in
if let strongSelf = self {
println("The name is \(strongSelf.name)")
}
}
init(personName: String) {
name = personName
}
deinit {
print("Person deinit \(self.name)")
}
}
func testFUN(){
let xiaoMing: Person = Person(personName: "XiaoMing")
xiaoMing.printName();
}
printName是self的属性,会被self持有,而它本身又在闭包内持有self,这导致了xiaoMing的deinit在自身超过作用域后还是没有被调用,也就是没有被释放。为了解决这种闭包内的循环引用,我们需要在闭包开始的时候添加一个标注,来表示这个闭包内的某些要素应该以何种特定的方式来使用。
如果我们可以确定在整个过程中self不会被释放的话,我们可以将上面的weak改为unowned,这样就不再需要strongSelf的判断。但是如果在过程中self被释放了而printName这个闭包没有被释放的话(比如生成Person后,某个外部变量持有了printName,随后这个Persone对象被释放了,但是printName已然存在并可能被调用),使用unowned将造成崩溃。在这里我们需要根据实际的需求来决定是使用weak还是unowned。
lazy懒加载
15.@autoreleasepool
16.值类型和引用类型
Swift中的struct和enum定义的类型是值类型,使用class定义的为引用类型。很有意思的是,Swift中的所有的内建类型都是值类型,不仅包括了传统意义像Int,Bool这些,甚至连String,Array以及Dictionary都是值类型的
在需要处理大量数据并且频繁操作(增减)其中元素时,选择NSMutableArray和NSMutableDictionary会更好,而对于容器内条目小而容器本身数目多的情况,应该使用Swift语言内建的Array和Dictionary。
17.Foundation框架
如果我们出于某种原因,确实需要NSString以及NSArray的话,我们需要显式的转换:
import Foundation
let string = "/var/controller_driver/secret_photo.png" as NSString
let fileName = components.lastObject as NSString
let fileName = components.lastObject as NSString
Swift的容器类型是可以装任意其他类型的,包括各种enum和struct,而NSArray和NSDictionary只能放NSObject的子类对象。所以在Array和Dictionary中如果装有非AnyObject或者不能转为AnyObject的内容的话,做强制的转换将会抛出编译错误
18 lazy懒加载
//简单表达式
lazy var first = NSArray(objects: "1","2")
//闭包
lazy var name:String = {
return "second"
}()
//不要忘记最后的小括号,只有加了小括号,闭包才会在掉用的时候立刻执行。
19.(指针)UnsafePointer
Swift定义了一套指针的访问和转换方法,那就是UnsafePointer和它的一系列变体。对于使用C API时如果遇到接受内存地址作为参数,或者返回是内存地址的情况,在Swift里会将它们转为UnsafePointer的类型
void method(const int *num) { printf("%d",*num);}
其对应的Swift方法应该是:
func method(num: UnsafePointer) {
print(num.memory)//memory属性读取存储的内容C语言中使用*
}
var a: CInt = 123
method(&a)//取地址都是&
注意对于C语言的基本数据和swift区别是在前面加CCInt,CBool和CChar
C APIconst Type *Type *
Swift APIUnsafePointerUnsafeMutablePointer//可变指针
说明:Swift不可变指针UnsafePointer ;可变指针UnsafeMutablePointer<>;
连续数据指针UnsafeBufferPointer;不透明指针COpaquePointer;方法指针CFunctionPointer
注意开辟指针需要自己释放
var pointer: UnsafeMutablePointer!
pointer = UnsafeMutablePointer.alloc(1)
pointer.initialize(MyClass())
println(pointer.memory.a)
pointer.destroy()
pointer.dealloc(1)
pointer = nil
malloc或者calloc开辟的指针需要使用free而不是dealloc
//
func CFArrayGetValueAtIndex(theArray: CFArray!, idx: CFIndex)->UnsafePointer{
}
20 ...和..<
for i in 0...3 { print(i) }
let test = "helLo"
let interval = "a"..."z"
for c in test {
if !interval.contains(String(c)) {
println("\(c)不是小写字母")
}
}
21.获取对象类型
OC:
NSDate *date = [NSDate date]; NSLog(@"%@",NSStringFromClass([date class])); //输出:__NSDate
[date class]与([NSDate class]区别输出:NSDate)
Swift:
let date = NSDate()
let name = NSStringFromClass(date.dynamicType)//结果__NSDate
//注意与下面区别
NSStringFromClass(NSDate.self)//结果NSDate
不能使用NSStringFromClass(NSDate.class)
在OC中使用
NSStringFromClass(NSDate.class) NSStringFromClass(NSDate.self)
注意:对于String结构体类型的无法使用上面的方法因为没有实现AnyObject接口
这里使用_stdlib_getTypeName和_stdlib_getDemangledTypeName??
22.AnyClass,元类型和.self
.self :.self可以用在类型后面取得类型本身,也可以用在某个实例后面取得这个实例本身
.Type :.Type表示的是某个类型的元类型
class A {
static var name: String = "zzzq"
class func testMethod(){
print("hello")
}
}
let typeA: A.Type = A.self//获得是该元类只能调用类方法或者静态属性
typeA.testMethod()
print(typeA.name)
.Type表示的是某个类型的元类型,而在Swift中,除了class,struct和enum这三个类型外,我们还可以定义protocol。对于protocol来说,有时候我们也会想取得接口的元类型。这时我们可以在某个protocol的名字后面使用.Protocol来获取,使用的方法和.Type是类似的
元类(metaclass):就是类对象不是实例对象
OC中:isa,实例对象的isa指向该类(存储实例方法和实例变量)该类的isa指向元类(存储类方法和静态变量)
23.接口和类方法中的Self L
//定义接口返回接口的定义时
在OC中接口写返回是instancetype类型来返回自己在swift中使用Self
protocol Copyable {
//应该做的是创建一个和接受这个方法的对象同样的东西,然后将其返回,返回的类型不应该发生改变,所以写为Self。然后开始尝试实现一个MyClass来满足这个接口:
func copy() -> Self
}
class CopyClass: Copyable {
var num = 1
func copy()-> Self{
/*错误代码=> let result = MyClass() result.num = num return result*/
//正确如下
let result = self.dynamicType.init()
return result
}
/* required关键字修饰的初始化方法
这是因为Swift必须保证当前类和其子类都能响应这个init方法*/
required init() {
}
}
24.自省
OC :
[obj1 isKindOfClass:[ClassA class]];
[obj2 isMemberOfClass:[ClassB class]];
swift :前提是NSObject子类
obj1.isKindOfClass(ClassA.self)
obj2.isMemberOfClass(ClassA.self)
使用is在功能上相当于原来的isKindOfClass
class,struct或enum类型都可以使用is判断
注意:编译器将对这种检查进行必要性的判断:如果编译器能够唯一确定类型,那么is的判断就没有必要,编译器将会抛出一个错误
25.动态类型和多方法
Swift中我们虽然可以通过dynamicType来获取一个对象的动态类型(也就是运行时的实际类型,而非代码指定或编译器看到的类型)但是在使用中,Swift现在却是不支持多方法的,也就是说,不能根据对象在动态时的类型进行合适的重载方法调用。
OC在同类中是不支持同名不同参数的重载但是在swift中是支持
26.局部
在OC中使用{}将代码局部区分局部有限
self.titleLabel = ({
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(150, 30, 20, 40)];
label.textColor = [UIColor redColor];
label.text = @"Title";
[view addSubview:label];
label;
});
swift中使用()
let titleLabel:UILabel = {
let label = UILabel(frame: CGRectMake(150, 30, 20, 40))
label.textColor = UIColor.redColor()
label.text = "Title"
self.view.addSubview(label)
return label
}()
对于OC使用{}局部区分swift自定义local方法
func local(closure: ()->()) {
closure()
}
local {
let titleLabel = UILabel(frame: CGRectMake(150, 30, 20, 40))
titleLabel.textColor = UIColor.redColor()
titleLabel.text = "Title"
}
27.Printable (2.0被CustomStringConvertible取代)和DebugPrintable (2.0被CustomDebugStringConvertible)
Swift中,大多数基础对象都遵循了CustomStringConvertible协议,比如Array、Dictionary(Swift 1.0中的Printable协议),该协议定义了description方法,用于print方法打印对象
通过extension扩展CustomStringConvertible实现description
28.访问级别
1.public:可以访问自己模块或应用中源文件里的任何实体,别人也可以访问引入该模块中源文件里的所有实体。通常情况下,某个接口或Framework是可以被任何人使用时,你可以将其设置为public级别。
2.internal(默认级别):可以访问自己模块或应用中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或Framework作为内部结构使用时,你可以将其设置为internal级别。
3.private:只能在当前源文件中使用的实体,称为私有实体。使用private级别,可以用作隐藏某些功能的实现细节。
注意:1.Framework的内部实现细节依然可以使用默认的internal级别,或者也可以定义为private级别。只有你想将它作为API的实体,才将其定义为public级别。
2.一个public类的所有成员的访问级别默认为internal级别。
3.元组的访问级别遵循它里面元组中最低级的访问级别。
4.枚举中成员的访问级别继承自该枚举。public enum CompassPoint {}该类型public前定义。
5.子类的访问级别不得高于父类的访问级别。比如说,父类的访问级别是internal,子类的访问级别就不能声明为public。
29.判等Equatable协议
swift中使用==判断字符串内容是否相同默认字符串实现Equatable协议可以重写该协议改变
注意这个协议在func == (lhs: String, rhs: String) -> Bool无需在实现协议中写应该放在全局
对于==的实现我们并没有像实现其他一些接口一样将其放在对应的extension里,而是放在了全局的scope中。这是合理的做法,因为你应该需要在全局范围内都能使用==。事实上,Swift的操作符都是全局的,关于操作符的更多信息,可以参看操作符。
class TodoItem {
let uuid: String
var title: String
init(uuid: String, title: String) {
self.uuid = uuid
self.title = title
}
}
extension TodoItem: Equatable {
}
//比较两个对象
func == (lhs: TodoItem, rhs: TodoItem) -> Bool {
return lhs.uuid == rhs.uuid
}
//比较字符串
func == (lhs: String, rhs: String) -> Bool {
return true
}
补:操作符(自定义运算符)支持重载操作符这样的特性
func +* (left: Vector2D, right: Vector2D) -> Double {
return left.x * right.x + left.y * right.y
}
注意:重载操作符时操作符是不能定义在局部域中的
要新加操作符的话,需要先对其进行声明,告诉编译器这个符号其实是一个操作符。添加如下代码
结合性(associativity)的值默认为none(left,right和none),优先级(precedence)默认为100。
表达:左结合的left,优先级为140 :associativity left precedence 140
infix operator +* {
associativity none precedence 160
}
infix
表示要定义的是一个中位操作符,即前后都是输入;其他的修饰子还包括prefix和postfix,不再赘述;
associativity
定义了结合律,即如果多个同类的操作符顺序出现的计算顺序。比如常见的加法和减法都是left,就是说多个加法同时出现时按照从左往右的顺序计算(因为加法满足交换律,所以这个顺序无所谓,但是减法的话计算顺序就很重要了)。点乘的结果是一个Double,不再会和其他点乘结合使用,所以这里写成none;
precedence
运算的优先级,越高的话越优先进行运算。Swift中乘法和除法的优先级是150,加法和减法是140,这里我们定义点积优先级160,就是说应该早于普通的乘除进行运算。
注意:Swift中对NSObject子类对象使用==时要是该子类没有实现这个操作符重载的话将回滚到-isEqual:方法
30.哈希Hashable协议
在Objective-C中NSObject中有一个-hash方法。当我们对一个NSObject的子类的-isEqual:进行重写的时候,我们一般也需要将-hash方法重写。
在Swift中,NSObject也默认就实现了Hashable,而且和判等的时候情况类似,NSObject对象的hashValue属性的访问将返回其对应的-hash的值。对于非NSObject的类,我们需要遵守Hashable并根据==操作符的内容给出哈希算法;而对于NSObject子类,需要根据是否需要在Objective-C中访问而选择合适的重写方式,去实现Hashable的hashValue或者直接重写NSObject的-hash方法
31.断言
注意:断言只会在Debug环境中有效,而在Release编译中所有的断言都将被禁用
/*当条件满足条件的时候break同时打印出自己的定义内容*/
assert(name == "zzqS", "it is zzq")
//OC中都是宏
/*当条件满足条件的时候break*/
assert(YES)
/*当条件不满足条件的时候break同时打印出自己的定义内容*/
NSAssert(NO, @"Unimplemented JSBadgeAligment type %@", @break");
32.fatalError
@noreturn:不再需要返回值
@noreturn func fatalError(message: StaticString,file: StaticString = default,line: UWord = default)
fatalError(@"遇到我会强制退出程序...")
在父类方法中写调用fatalError("这个方法必须在子类中被重写")子类重写覆盖该方法则不会调用父类fatalError方法
//父类标明了某个init方法是required的,但是你的子类永远不会使用这个方法来初始化时,就可以采用类似的方式,被广泛使用(以被广泛讨厌的) init(coder: NSCoder)就是一个例子
required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}
32.final:final关键字可以用在class,func或者var前面进行修饰,表示不允许对该内容进行继承或者重写操作
33.Swizzle
OC:
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalSelector = class_getInstanceMethod([UIButton class], @selector(sendAction:to:forEvent:));
Method swizzledSelector = class_getInstanceMethod([UIButton class], @selector(xxx_sendAction:to:forEvent:));
method_exchangeImplementations(originalSelector, swizzledSelector);
});
}
swift使用黑魔法替换UIButton的点击事件统计所有按钮被点击数目
extension UIButton {
class func xxx_swizzleSendAction() {
struct xxx_swizzleToken {
static var onceToken : dispatch_once_t = 0
}
dispatch_once(&xxx_swizzleToken.onceToken) {
let cls: AnyClass! = UIButton.self
let originalSelector = Selector("sendAction:to:forEvent:")
let swizzledSelector = Selector("xxx_sendAction:to:forEvent:")
let originalMethod =
class_getInstanceMethod(cls, originalSelector)
let swizzledMethod =
class_getInstanceMethod(cls, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
public func xxx_sendAction(action: Selector,
to: AnyObject!,
forEvent: UIEvent!)
{
struct xxx_buttonTapCounter {
static var count: Int = 0
}
xxx_buttonTapCounter.count += 1
print(xxx_buttonTapCounter.count)
xxx_sendAction(action, to: to, forEvent: forEvent)
}
}
注意:在Objective-C中我们一般在category的+load中完成,但是Swift的extension和Objective-C的category略有不同,extension并不是运行时加载的,因此也没有加载时候就会被调用的类似load的方法。另外,extension中也不应该做方法重写去覆盖load (其实重写也是无效的)最后我们看起来是在这个方法中调用了自己,似乎会形成一个死循环。但是因为我们实际上已经交换了sendAction:to:forEvent:和xxx_sendAction:to:forEvent:的实现,所以在做这个调用时恰好调用到的是原来的那个方法的实现。同理,在外部使用sendAction:to:forEvent:的时候(也就是点击按钮的时候),实际调用的实现会是我们在这里定义的带有计数器累加的实现
class Swizzler: NSObject {
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
UIButton.xxx_swizzleSendAction()
}
}
}
Swizzle使用了Objective-C的动态派发,对于NSObject的子类是可以直接使用的,但是对于Swift的类,因为默认并没有使用Objective-C运行时,因此也没有动态派发的方法列表,所以如果要Swizzle的是Swift类型的方法的话,我们需要将原方法和替换方法都加上dynamic标记,以指明它们需要使用动态派发机制。关于这方面的知识,可以参看@objc和dynamic的内容
34.lazy修饰符和lazy方法:
Swift中我们使用在变量属性前加lazy关键字的方式来简单地指定延时加载
class ClassA {
lazy var str: String = {
let str = "Hello"
print("只在首次访问输出")
return str
}() //注意有个()
}
简化:lazy var str: String = "Hello"
注意:1.只能声明属性是变量2.显式地指定属性类型
35.性能
在Objective-C对于NSObject的方法调用在编译是会被转为objc_megSend方法。运用OC运行时特性,使用动态派发的方式在运行时对方法进行查找。
个人认为最为靠近实际使用的标志性特性就是它的消息机制了,能到比较容易的做到很多静态语言很难做到的事情. Objective-C简单的运行时知识:
1.类和对象都是id,在给你一个id的前提下无法直观的知道这个对象是类对象还是类本身.简单的可以简化成runtime管理的都是id (id的本质其实是objc_object, objc_class头部其实就是id,也就是isa).
2. Class在objc中是动态创建的, selector, method, imp, protocol等都是随后绑定上去的(即所谓的运行时绑定).
3.通过runtime能够查出当前运行时环境中所有的类,每个类中的方法,每个类消息的绑定,每个类的实现的协议,每个协议的定义,每个类当前的消息缓存等一切你想知道的东西.
4.类的方法(消息)调用是间接的.
swift(静态语言)和objective-c(具有动态特性的静态语言)都是(编译语言)编译执行,swift比objective-c快的原因是,objective-c的方法是在运行时确定函数地址,编译时只记录方法名,运行时调用方法是根据方法名从哈希表中查找函数地址并跳跃到该地址执行代码,函数访问机制类似于C++的虚函数,大量时间浪费到查表上了。而swift的函数是在编译时确定函数地址,运行中直接跳跃到函数地址执行,无须查表,所以速度比objective-c快
但是swift使用继承NSObjct的类是具有动态特性的
36.Log输出
FILE String包含这个符号的文件的路径
LINE Int符号出现处的行号
COLUMN Int符号出现处的列
FUNCTION String包含这个符号的方法名字
func printLog(message: T,file: String = __FILE__,method: String = __FUNCTION__, line: Int = __LINE__){
print("\(file.lastPathComponent)[\(line)], \(method): \(message)")
}
37.Associated Object
swift中使用
private var key: Void
extension Class {
var title: String? {
get {
return objc_getAssociatedObject(self, &key) as? String
}
set {
objc_setAssociatedObject(self,&key, newValue,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
38.@asmname
无需通过c的h文件和-Bridging-Header.h文件导入C函数
@asmname("testCFun") funcctestCFun() -> Void
ctestCFun()直接调用
39.sizeofValue替换sizeof
sizeofValue(String)