如下内容均是笔者学习过程当中收集的知识点,顺序比较跳跃,初衷是为了方便查阅,顺便加深记忆。内容会不断更新,若是有什么问题或者有好的 Swift 方面的语法糖或者知识点也能够提出来,我会挑选斟酌后收录,欢迎你们关注~git
环境:github
Swift 4.0
Xcode 9.1
Objective-C 的 runtime 里的 Associated Object 容许咱们在使用 Category 扩展示有的类的功能的时候,直接添加实例变量。在 Swift 中 extension 不能添加存储属性,咱们能够利用 Associated Object 来实现,好比下面的 title
「实际上」是一个存储属性:shell
// MyClass.swift class MyClass {} // MyClassExtension.swift private var key: Void? extension MyClass { var title: String? { get { return swift_getAssociatedObject(self, &key) as? String } set { swift_setAssociatedObject(self, &key, newValue, .swift_ASSOCIATION_RETAIN_NONATOMIC) } } }
// 测试 func printTitle(_ input: MyClass) { if let title = input.title { print("Title: \(title)") } else { print("没有设置") } } let a = MyClass() printTitle(a) a.title = "Swifter.tips" printTitle(a) // 输出: // 没有设置 // Title: Swifter.tips”
Swift 中 Delegate 须要被声明成 weak
,来避免访问到已被回收的内存而致使崩溃,若是咱们像下面这样,是编译不过的:express
protocol MyClassDelegate { func method() } class MyClass { weak var delegate: MyClassDelegate? } class ViewController: UIViewController, MyClassDelegate { // ... var someInstance: MyClass! override func viewDidLoad() { super.viewDidLoad() someInstance = MyClass() someInstance.delegate = self } func method() { print("Do something") } //... } // 编译失败 // 'weak' may only be applied to class and class-bound protocol types, not 'MyClassDelegate'
这是由于 Swift 的 protocol 是能够被除了 class 之外的其余类型遵照的,而对于像 struct
或是 enum
这样的类型,自己就不经过引用计数来管理内存,因此也不可能用 weak
这样的 ARC 的概念来进行修饰。json
想要在 Swift 中使用 weak delegate,咱们就须要将 protocol 限制在 class 内:swift
@objc
关键字来达到,Objective-C 的 protocol 都只有类能实现,所以使用 weak 来修饰就合理了:@objc protocol MyClassDelegate { func method() }
class
,这能够为编译器显式地指明这个 protocol 只能由 class
来实现,避免了过多的没必要要的 Objective-C 兼容:protocol MyClassDelegate: class { func method() }
Objective-C 中的 protocol 里存在 @optional
关键字,被这个关键字修饰的方法并不是必需要被实现,原生的 Swift protocol 里没有可选项,全部定义的方法都是必须实现的,若是不是实现是没法编译的:数组
class ViewController: UIViewController,MyProtocol { } // 编译失败 // Type 'ViewController' does not conform to protocol 'MyProtocol'
若是咱们想要像 Objective-C 里那样定义可选的协议方法,就须要将协议自己和可选方法都定义为 Objective-C 的,也即在 protocol 定义以前加上 @objc
,方法以前加上 @objc optional
:安全
@objc protocol MyProtocol { @objc optional func myMethod() }
另外,对于全部的声明,它们的前缀修饰是彻底分开的,也就是说你不能像是在 Objective-C 里那样用一个 @optional
指定接下来的若干个方法都是可选的了,必须对每个可选方法添加前缀,对于没有前缀的方法来讲,它们是默认必须实现的:性能优化
@objc protocol MyProtocol { @objc optional func optionalMethod() // 可选 func necessaryMethod() // 必须 @objc optional func anotherOptionalMethod() // 可选 }
一个不可避免的限制是,使用 @objc
修饰的 protocol 就只能被 class
实现了,也就是说,对于 struct
和 enum
类型,咱们是没法令它们所实现的协议中含有可选方法或者属性的。另外,实现它的 class
中的方法还必须也被标注为 @objc
,或者整个类就是继承自 NSObject
。对于这种问题,在 Swift 2.0 中,咱们有了另外一种选择,那就是使用 protocol extension。咱们能够在声明一个 protocol 以后再用 extension 的方式给出部分方法默认的实现,这样这些方法在实际的类中就是可选实现的了:闭包
protocol MyProtocol { func optionalMethod() // 可选 func necessaryMethod() // 必须 func anotherOptionalMethod() // 可选 } extension MyProtocol { //默认的可选实现 func optionalMethod() { print("optionalMethod") } //默认的可选实现 func anotherOptionalMethod() { print("anotherOptionalMethod") } }
class ViewController: UIViewController,MyProtocol { // 必须的实现 func necessaryMethod() { print("necessaryMethod") } override func viewDidLoad() { super.viewDidLoad() self.optionalMethod(); self.necessaryMethod(); self.anotherOptionalMethod(); } } // 输出: // optionalMethod // necessaryMethod // necessaryMethod
Swift 中的单例很是简单,Swift 1.2 以及以后:
class Singleton { static let sharedInstance = Singleton() private init() {} }
这种写法不可是线程安全的,也是懒加载的,let
定义的属性自己就是线程安全的,同时 static
定义的是一个 class constant,拥有全局做用域和懒加载特性。
另外,这个类型中加入了一个私有的初始化方法,来覆盖默认的公开初始化方法,这让项目中的其余地方不可以经过 init 来生成本身的 Singleton
实例,也保证了类型单例的惟一性。若是你须要的是相似 default 的形式的单例 (也就是说这个类的使用者能够建立本身的实例) 的话,能够去掉这个私有的 init
方法。
在 Objective-C 中的 %@
这样的格式在指定的位置设定占位符,而后经过参数的方式将实际要输出的内容补充完整。例如 Objective-C 中经常使用的向控制台输出的 NSLog
方法就使用了这种格式化方法:
float a = 1.234567; NSString *b = @"Helllo"; NSLog(@"float:%.2f str:%p",a,b); // 输出: // float:1.23 str:0x1024a1078
对应 Swift 中咱们能够这样:
let a = 1.234567 let b = "Helllo" let c = String(format:"float:%.2f str:%p",a,b) print(c) // 输出: // float:1.23 str:0x604000249e10
@selector
是 Objective-C 时代的一个关键字,它能够将一个方法转换并赋值给一个 SEL 类型,它的表现很相似一个动态的函数指针。在 Swift 中没有 @selector
了,取而代之,从 Swift 2.2 开始咱们使用 #selector
来从暴露给 Objective-C 的代码中获取一个 selector
,而且由于 selector
是 Objective-C runtime 的概念,在 Swift 4 中,默认状况下全部的 Swift 方法在 Objective-C 中都是不可见的,因此你须要在这类方法前面加上 @objc
关键字,将这个方法暴露给 Objective-C,才能进行使用:
let btn = UIButton.init(type: .system) btn.backgroundColor = UIColor.red btn.frame = CGRect(x: 100, y: 100, width: 150, height: 40) btn.setTitle("Button", for: .normal) //无参数 btn.addTarget(self, action: #selector(btnClick), for: .touchUpInside) view.addSubview(btn) @objc func btnClick() { print("button click !") }
... //有参数 btn.addTarget(self, action: #selector(btnClick(_ :)), for: .touchUpInside) ... @objc func btnClick(_ button: UIButton) { print("button click !") }
Swift 的 protocol 不只能够被 class 类型实现,也适用于 struct
和 enum
,由于这个缘由,咱们在写给别人用的协议时须要多考虑是否使用 mutating
来修饰方法。Swift 的 mutating
关键字修饰方法是为了能在该方法中修改 struct
或是 enum
的变量,因此若是你没在协议方法里写 mutating
的话,别人若是用 struct
或者 enum
来实现这个协议的话,就不能在方法里改变本身的变量了,好比下面的代码是编译不过的:
protocol Vehicle { func changeColor() } struct MyCar: Vehicle { var color = "blue" func changeColor() { color = "red" } } // 编译失败 // Cannot assign to property: 'self' is immutable
咱们应该加上 mutating
关键字:
protocol Vehicle { mutating func changeColor() } struct MyCar: Vehicle { var color = "blue" mutating func changeColor() { color = "red" } } override func viewDidLoad() { super.viewDidLoad() var car = MyCar() print(car.color) car.changeColor() print(car.color) } // 输出: // blue // 输出: // red
使用 NSArray 时一个很常碰见的的需求是在枚举数组内元素的同时也想使用对应的下标索引,在 Objective-C 中最方便的方式是使用 NSArray 的 enumerateObjectsUsingBlock:
,在 Swift 中存在一个效率,安全性和可读性都很好的替代,那就是快速枚举某个数组的EnumerateGenerator
,它的元素是同时包含了元素下标索引以及元素自己的多元组:
let arr = ["a","b","c","d","e"] for (idx, str) in arr.enumerated() { print("idx: \(idx) str: \(str)") } // 输出: idx: 0 str: a idx: 1 str: b idx: 2 str: c idx: 3 str: d idx: 4 str: e
函数参数默认是常量,若是试图在函数体中更改参数值将会致使编译错误,好比下面的例子中尝试着交换值:
func swapTwoInts(_ a: Int, _ b: Int) { let temporaryA = a a = b b = temporaryA } // 编译失败 // Cannot assign to value: 'a' is a 'let' constant // Cannot assign to value: 'b' is a 'let' constant
若是想要一个函数能够修改参数的值,而且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters):
func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA }
Swift 的方法是支持默认参数的,也就是说在声明方法时,能够给某个参数指定一个默认使用的值。在调用该方法时要是传入了这个参数,则使用传入的值,若是缺乏这个输入参数,那么直接使用设定的默认值进行调用。和其余不少语言的默认参数相比较,Swift 中的默认参数限制更少,并无所谓 "默认参数以后不能再出现无默认值的参数"这样的规则,举个例子,下面两种方法的声明在 Swift 里都是合法可用的:
func sayHello1(str1: String = "Hello", str2: String, str3: String) { print(str1 + str2 + str3) } func sayHello2(str1: String, str2: String, str3: String = "World") { print(str1 + str2 + str3) }
sayHello1(str2: " ", str3: "World") sayHello2(str1: "Hello", str2: " ") //输出都是 Hello World
延时加载或者说延时初始化是很经常使用的优化方法,在构建和生成新的对象的时候,内存分配会在运行时耗费很多时间,若是有一些对象的属性和内容很是复杂的话,这个时间更是不可忽略。另外,有些状况下咱们并不会当即用到一个对象的全部属性,而默认状况下初始化时,那些在特定环境下不被使用的存储属性,也同样要被初始化和赋值,也是一种浪费。在 Objective-C 中,一个延迟加载通常是这样的:
// ClassA.h @property (nonatomic, copy) NSString *testString; // ClassA.m - (NSString *)testString { if (!_testString) { _testString = @"Hello"; NSLog(@"只在首次访问输出"); } return _testString; }
对应在 Swift 中,使用 lazy
做为属性修饰符时,只能声明属性是变量,且咱们须要显式地指定属性类型,不然会编译错误:
class ClassA { lazy let str: String = { let str = "Hello" print("只在首次访问输出") return str }() } // 编译失败 // 'lazy' cannot be used on a let class ClassA { lazy var str = { let str = "Hello" print("只在首次访问输出") return str }() } // 编译失败 // Unable to infer complex closure return type
咱们应该声明为 var
并指定好类型:
class ClassA { lazy var str: String = { let str = "Hello" print("只在首次访问输出") return str }() } override func viewDidLoad() { super.viewDidLoad() let ca = ClassA() print(ca.str) print(ca.str) print(ca.str) } // 输出: // 只在首次访问输出 // Hello // Hello // Hello
若是不须要作什么额外工做的话,也能够对这个 lazy
的属性直接写赋值语句:
lazy var str: String = "Hello"
咱们还能够利用 lazy
配合像 map
或是 filter
这类接受闭包并进行运行的方法一块儿,让整个行为变成延时进行的。在某些状况下这么作也对性能会有不小的帮助。例如,直接使用 map 时:
let data = 1...3 let result = data.map { (i: Int) -> Int in print("正在处理 \(i)") return i * 2 } print("准备访问结果") for i in result { print("操做后结果为 \(i)") } print("操做完毕") // 输出: // 正在处理 1 // 正在处理 2 // 正在处理 3 // 准备访问结果 // 操做后结果为 2 // 操做后结果为 4 // 操做后结果为 6 // 操做完毕
而若是咱们先进行一次 lazy
操做的话,咱们就能获得延时运行版本的容器:
let data = 1...3 let result = data.lazy.map { (i: Int) -> Int in print("正在处理 \(i)") return i * 2 } print("准备访问结果") for i in result { print("操做后结果为 \(i)") } print("操做完毕") // 准备访问结果 // 正在处理 1 // 操做后结果为 2 // 正在处理 2 // 操做后结果为 4 // 正在处理 3 // 操做后结果为 6 // 操做完毕
对于那些不须要彻底运行,可能提早退出的状况,使用 lazy 来进行性能优化效果会很是有效。
在 Objective-C 中,咱们常常在代码中插入 #param
符号来标记代码的区间,这样在 Xcode 的导航栏中咱们就能够看到组织分块后的方法列表。在 Swift 中咱们能够用 MARK:
来代替:
在 Objective-C 中还有一个很经常使用的编译标记,那就是 #warning
,一个 #warning
标记能够在 Xcode 的代码编辑器中显示为明显的黄色警告条,很是适合用来提示代码的维护者和使用者须要对某些东西加以关注。在 Swift 中咱们能够用 FIXME:
和 TODO:
配合 shell
来代替:
脚本:
TAGS="TODO:|FIXME:" echo "searching ${SRCROOT} for ${TAGS}" find "${SRCROOT}" \( -name "*.swift" \) -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($TAGS).*\$" | perl -p -e "s/($TAGS)/ warning: \$1/"
效果:
在 Swift 3 中,须要换行时是须要 \n
:
let str = "xxxx\nxxx" // 输出: // xxxx // xxx
在 swift 4 中,咱们可使用 """
:
let jsonStr = """ { "id": 123455, "nickname": "xxxx", "isMale": true, "birthday": "2000年3月24日", "personalURL": "https://xxxxxx.github.io" } """ // 输出: { "id": 123455, "nickname": "xxxx", "isMale": true, "birthday": "2000年3月24日", "personalURL": "https://xxxxxx.github.io" }
咱们须要切割某个字符串时能够用 split
方法,须要注意的是,返回的结果是个数组:
let str = "Hello,world !" print(str.split(separator: ",")) // 输出: // ["Hello", "world !"]
class MyClass { var name = "ifelseboyxx" }
Swift 4 中 Apple 引入了新的 KeyPath 的表达方式,如今,对于类型 MyClass
中的变量 name
,对应的 KeyPath 能够写为 \MyClass.name
,利用 KVC 修改 name
值的话,咱们能够这么操做:
let object = MyClass() print("name: \(object.name)") // set object[keyPath: \MyClass.name] = "ifelseboy" // get print("name: \(object[keyPath: \MyClass.name])") // 输出: // name: ifelseboyxx // name: ifelseboy
另外 Swift 4 中 struct
一样支持 KVC :
struct MyStruct { var age: Int } var obj = MyStruct(age: 18) print("我今年 \(obj.age) 岁了") obj[keyPath: \MyStruct.age] = 8 print("我今年 \(obj[keyPath: \MyStruct.age]) 岁了") // 输出: // 我今年 18 岁了 // 我今年 8 岁了
KVC 一节中代码里有个注意点:
var obj = MyStruct(age: 18) //替换为 let obj = MyStruct(age: 18)
是编译不过的,会报错:
Cannot assign to immutable expression of type 'Int'
笔者初次也犯了这样的错误,想固然的认为 MyClass
用 let
声明的是没有问题的,struct
也同样:
let object = MyClass()
其实缘由很简单,swift 中 Class 是引用类型的,而 struct 是值类型的:值类型在被赋给一个变量,或被传给函数时,实际是作了一次拷贝。引用类型在被赋给一个变量,或被传给函数时,传递的是引用。
很遗憾,依然只有 NSObject
才能支持 KVO,另外因为 Swift 为了效率,默认禁用了动态派发,所以想用 Swift 来实现 KVO,咱们还须要作额外的工做,那就是将想要观测的对象标记为 dynamic
和 @objc
,下面的 ? 是 ViewController
监听 MyClass
的 date
属性:
class MyClass: NSObject { @objc dynamic var date = Date() } class ViewController: UIViewController { var myObject: MyClass! var observation: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() myObject = MyClass() print("当前日期:\(myObject.date)") observation = myObject.observe(\MyClass.date, options: [.old,.new], changeHandler: { (_, change) in if let newDate = change.newValue , let oldDate = change.oldValue { print("日期发生变化 old:\(oldDate) new:\(newDate) ") } }) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { self.myObject.date = Date() } } } // 输出: // 当前日期:2017-12-07 06:31:26 +0000 // 日期发生变化 old:2017-12-07 06:31:26 +0000 new:2017-12-07 06:31:27 +0000
在 Objective-C 中咱们几乎能够没有限制地对全部知足 KVC 的属性进行监听,而如今咱们须要属性有 dynamic
和 @objc
进行修饰。大多数状况下,咱们想要观察的类包含这两个修饰 (除非这个类的开发者有意为之,不然通常也不会有人愿意多花功夫在属性前加上它们,由于这毕竟要损失一部分性能),而且有时候咱们极可能也没法修改想要观察的类的源码。遇到这样的状况的话,一个可能可行的方案是继承这个类而且将须要观察的属性使用 dynamic
和 @objc
进行重写。好比刚才咱们的 MyClass
中若是 date
没有相应标注的话,咱们可能就须要一个新的 MyChildClass
了:
class MyClass: NSObject { var date = Date() } class MyChildClass: MyClass { @objc dynamic override var date: Date { get { return super.date } set { super.date = newValue } } } class ViewController: UIViewController { var myObject: MyChildClass! var observation: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() myObject = MyChildClass() print("当前日期:\(myObject.date)") observation = myObject.observe(\MyChildClass.date, options: [.old,.new], changeHandler: { (_, change) in if let newDate = change.newValue , let oldDate = change.oldValue { print("日期发生变化 old:\(oldDate) new:\(newDate) ") } }) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { self.myObject.date = Date() } } } // 输出: // 当前日期:2017-12-07 06:36:50 +0000 // 日期发生变化 old:2017-12-07 06:36:50 +0000 new:2017-12-07 06:36:51 +0000
在 Objective-C 中,若是咱们想叠加按钮的某个状态,能够这么写:
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom]; [button setTitle:@"Test" forState:UIControlStateNormal | UIControlStateSelected];
对应的 Swift 咱们能够这么写:
let btn = UIButton.init(type: .custom) btn.setTitle("hehe", for: [.normal ,.selected])
把须要叠加的状态用个数组装起来就好了。
在 Objective-C 中,咱们能够用 @synchronized
这个关键字能够用来修饰一个变量,并为其自动加上和解除互斥锁。这样,能够保证变量在做用范围内不会被其余线程改变:
- (void)myMethod:(id)anObj { @synchronized(anObj) { // 在括号内持有 anObj 锁 } }
虽然这个方法很简单好用,可是很不幸的是在 Swift 中它已经 (或者是暂时) 不存在了。其实 @synchronized
在幕后作的事情是调用了 objc_sync
中的 objc_sync_enter
和 objc_sync_exit
方法,而且加入了一些异常判断。所以,在 Swift 中,若是咱们忽略掉那些异常的话,咱们想要 lock 一个变量的话,能够这样写:
func myMethod(anObj: AnyObject!) { objc_sync_enter(anObj) // 在 enter 和 exit 之间持有 anObj 锁 objc_sync_exit(anObj) }
更进一步,若是咱们喜欢之前的那种形式,甚至能够写一个全局的方法,并接受一个闭包,来将 objc_sync_enter
和 objc_sync_exit
封装起来:
func synchronized(_ lock: AnyObject, closure: () -> ()) { objc_sync_enter(lock) closure() objc_sync_exit(lock) } // 使用: synchronized(self) { }
这样使用起来就和 Objective-C 中 @synchronized
很像了。
再举个 ? ,若是咱们想为某个类实现一个线程安全的 setter
,能够这样:
class Obj { var _str = "123" var str: String { get { return _str } set { synchronized(self) { _str = newValue } } } }
在 Objective-C 中,咱们一般会自定义日志输出来完善信息以及避免 release
下的输出,好比下面这种,能够额外提供行数、方法名等信息:
#ifdef DEBUG #define XXLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) #else #define XXLog(...) #endif
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. XXLog(@"ifelseboyxx"); return YES; } // 输出: // 2017-12-08 13:32:02.211306+0800 Demo[17902:88775537] -[AppDelegate application:didFinishLaunchingWithOptions:] [Line 28] ifelseboyxx
在 Swift 中,咱们能够这样自定义:
func xxprint<T>(_ message: T, filePath: String = #file, line: Int = #line, function: String = #function) { #if DEBUG let fileName = (filePath as NSString).lastPathComponent.replacingOccurrences(of: ".Swift", with: "") let dateFormatter = DateFormatter() dateFormatter.locale = Locale.current dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" print("[" + dateFormatter.string(from: Date()) + " " + fileName + " " + function + " \(line)" + "]:" + "\(message)") #endif }
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() xxprint("ifelseboyxx") DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { xxprint("ifelseboyxx") } } } // 输出: // [2017-12-08 13:49:38 ViewController.swift viewDidLoad() 27]:ifelseboyxx // [2017-12-08 13:49:39 ViewController.swift viewDidLoad() 29]:ifelseboyxx
在 Objective-C 中,咱们一般把属性声明为 readonly
来提醒别人:“不要修改!!”,一般这么写:
@interface Person : NSObject @property (nonatomic, readonly, copy) NSString *name; @end
若是外部尝试修改的话,会编译错误:
- (void)viewDidLoad { [super viewDidLoad]; Person *p = [Person new]; p.name = @"ifelseboyxx"; } // 编译错误: // Assignment to readonly property
有些状况下,咱们但愿内部能够点语法访问 name
属性,也就是 self.name
,可是由于是 readonly
的,会编译错误:
@implementation Person - (instancetype)init { self = [super init]; if (self) { self.name = @"ifelseboyxx"; } return self; } @end // 编译错误: // Assignment to readonly property
这时候咱们就会在内部的 extension
从新声明一个 readwrite
的一样的属性,也就是“外部只读,内部可写”:
@interface Person () @property (nonatomic, readwrite, copy) NSString *name; @end
在 Swift 中,咱们可能有一样的场景。这里就不得不提到 private
和 fileprivate
关键字了。private
表示声明为私有的实体只能在其声明的范围内被访问。好比我在 MyClass
中声明了一个私有的 name
属性,外部访问的话会编译错误:
class MyClass { private var name: String = "Test" } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let only = MyClass() print(only.name) only.name = "ifelseboyxxv587" } } // 编译异常: // 'name' is inaccessible due to 'private' protection level
而 fileprivate
,看命名咱们大概能猜到,就是将对实体的访问权限于它声明的源文件。通俗点讲,好比我上面的代码都是在 ViewController.swift
这个文件里的,我把 private
修改成 fileprivate
,就不会编译错误了:
class MyClass { fileprivate var name: String = "Test" }
那么若是非 ViewController.swift
文件,也想访问 MyClass
的 name
属性该怎么办呢?咱们能够把 name
属性声明为 fileprivate(set)
,就要就达到相似 Objective-C 中的 readonly
效果了 :
// ViewController.swift 文件 class MyClass { fileprivate(set) var name: String = "Test" } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let only = MyClass() print(only.name) only.name = "ifelseboyxxv587" print(only.name) } } // 编译正常,ViewController.swift 文件内可读可写 // 输出: // Test // ifelseboyxxv587
// AppDelegate.swift 文件 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. let only = MyClass() print(only.name) //只能读 only.name = "ifelseboyxxv587" //这里报错,不能写 return true } // 编译异常: // Cannot assign to property: 'name' setter is inaccessible
在 Objective-C 中,咱们能够利用 {}
来开辟新的做用域,来避免对象名称重复的问题:
NSString *ha = @"测试一"; { NSString *ha = @"测试二"; NSLog(@"%@",ha); } NSLog(@"%@",ha); // 输出: // 2017-12-11 16:55:20.303132+0800 Demo[48418:93027416] 测试二 // 2017-12-11 16:55:20.303316+0800 Demo[48418:93027416] 测试一
在 Swift 中,取代 {}
的是 do {}
:
let ha = "测试一" do { let ha = "测试二" print(ha) } print(ha) // 输出: // 测试二 // 测试一
在 Objective-C 中,咱们若是想倒序数组通常这样:
NSArray *array = @[@"1",@"2",@"3"]; NSArray *reversedArray = [[array reverseObjectEnumerator] allObjects]; // 输出: // 2017-12-11 17:39:57.127466+0800 Demo[49004:93210504] ( 3, 2, 1 )
在 Swift 中,相对简单点:
let arr:[String] = ["1","2","3"] let reversedArr:[String] = arr.reversed() // 输出: // ["3", "2", "1"]
在 Objective-C 中,若是遇到多层嵌套的条件语句,咱们若是想要指定跳出某个条件语句是很不方便的。好比有两个循环,一旦找到它们相同的,就马上中止循环,咱们可能会这么作:
NSArray *arr1 = @[@"1",@"2",@"3",@"4",@"5"]; NSArray *arr2 = @[@"4",@"6",@"8",@"9",@"2"]; BOOL finded = NO; for (NSString *x in arr1) { if (finded) { break; } NSLog(@"x:%@",x); for (NSString *y in arr2) { NSLog(@"y:%@",y); if ([x isEqualToString:y]) { NSLog(@"找到相等的了:%@",x); finded = YES; break; } } }
咱们须要借助 finded
这个 BOOL
,来方便咱们跳出循环。在 Swift 中,咱们就能够利用标签语句,来指定具体跳出哪一个循环,语法是这样的:
标签名: 条件语句 { }
上面的 ? 咱们能够这么写:
let arr1 = ["1","2","3","4","5"] let arr2 = ["4","6","8","9","2"] label: for x in arr1 { print("x: \(x)") for y in arr2 { print("y: \(y)") if x == y { print("找到相等的了:\(y)") break label } } }
上面代码,咱们把第一层循环定义了标签:label
。在第二层循环中,一旦条件成立,马上跳出第一层循环 label
。这个特性,能够说十分方便了!
在 Objective-C 中,咱们自定义通知时,对于名称的定义通常都有规范:
// xxx.h UIKIT_EXTERN NSString * const XXXXNotification; // xxx.m NSString * const XXXXNotification = @"XXXXNotification";
在 Swift 中,咱们能够参考 Alamofire 的方式,建立个专门存放通知名的文件,扩展 Notification.Name
并以结构体 struct
方式声明:
// XXNotification.swift 文件 import Foundation extension Notification.Name { public struct Task { public static let 通知名 = Notification.Name(rawValue: "org.target名称.notification.name.task.通知名") } }
而后咱们就能够愉快的使用了:
// add NotificationCenter.default.addObserver(self, selector: #selector(myNotification(_ :)), name: NSNotification.Name.Task.通知名, object: self) // post NotificationCenter.default.post(name: NSNotification.Name.Task.通知名, object: self) // remove NotificationCenter.default.removeObserver(self, name: NSNotification.Name.Task.通知名, object: self)