Swift的字符串类型String
,和OC的NSString
,在API设计上还是有较大差异的
// 空字符串
var emptyStr1 = ""
var emptyStr2 = String()
// 拼接字符串
var str: String = "1"
str.append("_2")
// 重载运算符
str = str + "_3"
str += "_4"
// 插值
str = "\(str)_5"
print(str, str.count) // 1_2_3_4_5, 9
// 字符串的判断
var str = "123456"
print(str.hasPrefix("123")) // true
print(str.hasSuffix("456")) // true
String的插入和删除
var str = "1_2"
str.insert("_", at: str.endIndex) // 1_2_
str.insert(contentsOf: "3_4", at: str.endIndex) // 1_2_3_4
str.insert(contentsOf: "666", at: str.index(after: str.startIndex)) // 1666_2_3_4
str.insert(contentsOf: "888", at: str.index(before: str.endIndex)) // 1666_2_3_8884
str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4)) // 1666hello_2_3_8884
str.remove(at: str.firstIndex(of: "1")!) // 666hello_2_3_8884
str.removeAll { Substring == "6" } // hello_2_3_8884
let range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex)
str.removeSubrange(range) // hello_2_3_4
String
下标、prefix、suffix
可以通过String
等截取子串,子串类型不是Substring
,而是var str = "1_2_3_4_5"
var substr1 = str.prefix(3) // 1_2
var substr2 = str.suffix(3) // 4_5
var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3)
var substr3 = str[range] // 1_2
// 最初的String
print(substr3.base) // 1_2_3_4_5
// Substring -> String
var str2 = String(substr3)
Substring
base
和它的Substring
,共享字符串数据
其本质是String
内部有一个指针指向Substring
对应的区域
String
发生修改或者转为String
时,会分配新的内存存储字符串数据,不会影响到最初的String与Character的内容,编译器会自动做优化
for c in "jack" { // c是Character类型
print(c)
}
var str = "jack"
var c = str[str.startIndex] // c是Character类型
String相关的协议
BidirectionalCollection
startIndex
协议包含的部分内容
endIndex
、index
属性、String
方法Array
、RangeReplaceableCollection
都遵守了这个协议append
协议包含的部分内容
insert
、remove
、String
方法Array
、Dictionary
都遵守了这个协议Set
、多行String也有实现上述协议中声明的一些方法,只是并没有遵守上述协议
let str = """
1
”2“
3
'4'
"""
let str = """
Escaping the first quote \"""
Escaping two quotes \"\""
Escaping all three quotes \"\"\"
"""
如果要显示3引号,至少转义1个引号
let str1 = "These are the same."
let str2 = """
These are the same.
"""
以下两个字符是等价的
let str = """
1
2
3
4
"""
缩进以结尾的3引号为对齐线
String和NSString
String
NSString
和String
之间可以随时随地的桥接转换
如果你觉得String
的API过于复杂难用,可以考虑将NSString
转为var str1: String = "jack"
var str2: NSString = "rose"
var str3 = str1 as NSString
var str4 = str2 as String
var str5 = str3.substring(with: NSRange(location: 0, length: 2))
print(str5) // ja
String
我们通过反汇编发现,NSString
和比较字符串内容是否等价的转换会调用函数来实现的,相对会有性能的消耗,但由于编译器的优化,消耗的成本可以忽略不计
String
==
使用NSString
运算符isEqual
使用==
方法,也可以使用isEqual
运算符(本质还是调用了var str1: NSString = "jack"
var str2: String = "rose"
var str5: String = "rose"
var str6: NSString = "jack"
print(str2 == str5)
print(str1 == str6)
方法)==
通过反汇编,我们可以看到isEqual
运算符的本质还是调用了下面是Swift和OC的几个类型的转换表格方法
String
NSString
和NSMutableString
可以相互转换,而String
就只能单向转换成
其他类型同理
AnyObject、class、@objc
如果协议对应@objc
来修饰,那么只能被类所遵守
被// Swift文件
@objc protocol Runnable4 {
func run()
}
// OC文件
@interface LLTest : NSObject<Runnable4>
@end
@implementation LLTest
- (void)run { }
@end
修饰的协议,还可以暴露给OC去遵守协议实现
@objc
可以通过class
定义可选协议,这种协议只能被@objc protocol Runnable4 {
func run()
@objc optional func eat()
}
class Person: Runnable4 {
func run() {
print("run")
}
}
遵守
dynamic
@objc dynamic
被Runtime
修饰的内容会具有动态性,比如调用方法会走class Dog {
@objc dynamic func test1() {}
func test2() {}
}
var d = Dog()
d.test1()
d.test2()
的消息发送机制
混编调用的本质
具体汇报调用过程可以参考上文
KVC、KVO
Swift支持NSObject
的条件需要属性所在的类、监听器最终继承自@objc dynamic
用class Observer: NSObject {
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("observeValue", change?[.newKey] as Any)
}
}
class Person: NSObject {
@objc dynamic var age: Int = 0
var observer: Observer = Observer()
override init() {
super.init()
addObserver(observer, forKeyPath: "age", options: .new, context: nil)
}
deinit {
removeObserver(observer, forKeyPath: "age")
}
}
var p = Person()
p.age = 20
p.setValue(25, forKey: "age")
// Optional(20)
// Optional(25)
修饰对应的属性
block
KVO
方式的class Person: NSObject {
@objc dynamic var age: Int = 0
var observation: NSKeyValueObservation?
override init() {
super.init()
observation = observe(\Person.age, options: .new, changeHandler: { (person, change) in
print(change.newValue as Any)
})
}
}
var p = Person()
p.age = 20
p.setValue(25, forKey: "age")
// Optional(20)
// Optional(25)
关联对象(Associated Object)
class
在Swift中,extension
依然可以使用关联对象
默认情况下,extension
不可以增加存储属性
借助关联对象,可以实现类似class
为class Person {}
extension Person {
// Void类型只占一个字节
private static var AGE_KEY: Void?
var age: Int {
get {
(objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0
}
set {
objc_setAssociatedObject(self, &Self.AGE_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
}
var p = Person()
print(p.age) // 0
p.age = 10
print(p.age) // 10
增加存储属性的效果
资源名管理
let img = UIImage(named: "logo")
let btn = UIButton(type: .custom)
btn.setTitle("添加", for: .normal)
performSegue(withIdentifier: "login_main", sender: self)
我们日常在代码中对资源的使用如下
枚举
我们采用Android
的方式对资源名进行管理
这种方式是参考了enum R {
enum string: String {
case add = "添加"
}
enum image: String {
case logo
}
enum segue: String {
case login_main
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let img = UIImage(named: R.image.logo)
let btn = UIButton(type: .custom)
btn.setTitle(R.string.add, for: .normal)
performSegue(withIdentifier: R.segue.login_main, sender: self)
}
}
extension UIImage {
convenience init?(named name: R.image) {
self.init(named: name.rawValue)
}
}
extension UIViewController {
func performSegue(withIdentifier identifier: R.segue, sender: Any?) {
performSegue(withIdentifier: identifier.rawValue, sender: sender)
}
}
extension UIButton {
func setTitle(_ title: R.string, for state: UIControl.State) {
setTitle(title.rawValue, for: state)
}
}
的资源名管理方式
let img = UIImage(named: "logo")
let font = UIFont(name: "Arial", size: 14)
资源名管理的其他思路
原始写法如下
enum R {
enum image {
static var logo = UIImage(named: "logo")
}
enum font {
static func arial(_ size: CGFloat) -> UIFont? {
UIFont(name: "Arial", size: size)
}
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let img = R.image.logo
let font = R.font.arial(14)
}
}
多线程开发
更多优秀的思路请参考以下链接:
https://github.com/mac-cain13/R.swifthttps://github.com/SwiftGen/SwiftGen
DispatchWorkItem
利用public typealias Task = () -> Void
public struct Asyncs {
/// 异步执行
public static func async(_ task: @escaping Task) {
_async(task)
}
public static func async(_ task: @escaping Task,
_ mainTask: @escaping Task) {
_async(task, mainTask)
}
/// 主线程延迟执行
@discardableResult
public static func delay(_ seconds: Double,
_ block: @escaping Task) -> DispatchWorkItem {
let item = DispatchWorkItem(block: block)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
return item
}
/// 异步延迟执行
@discardableResult
public static func asyncDelay(_ seconds: Double,
_ task: @escaping Task) -> DispatchWorkItem {
_asyncDelay(seconds, task)
}
@discardableResult
public static func asyncDelay(_ seconds: Double,
_ task: @escaping Task,
_ mainTask: @escaping Task) -> DispatchWorkItem {
_asyncDelay(seconds, task, mainTask)
}
}
// MARK: - 私有API
extension Asyncs {
private static func _async(_ task: @escaping Task,
_ mainTask: Task? = nil) {
let item = DispatchWorkItem(block: task)
DispatchQueue.global().async(execute: item)
if let main = mainTask {
item.notify(queue: DispatchQueue.main, execute: main)
}
}
private static func _asyncDelay(_ seconds: Double,
_ task: @escaping Task,
_ mainTask: Task? = nil) -> DispatchWorkItem {
let item = DispatchWorkItem(block: task)
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
if let main = mainTask {
item.notify(queue: DispatchQueue.main, execute: main)
}
return item
}
}
封装常用多线程执行函数
dispatch_once
类型属性
在Swift中已被废弃,取而代之的是用全局变量\常量
或者lazy+dispatch_once
默认自带fileprivate let initTask2: Void = {
print("initTask2")
}()
class ViewController: UIViewController {
static let initTask1: Void = {
print("initTask1------------")
}()
override func viewDidLoad() {
super.viewDidLoad()
let _ = Self.initTask1
let _ = initTask2
}
}
效果
class Cache {
private static var data = [String : Any]()
private static var lock = DispatchSemaphore(value: 1)
static func set(_ key: String, _ value: Any) {
lock.wait()
defer { lock.signal() }
data[key] = value
}
}
多个线程操作同一份数据会有资源抢夺问题,需要进行加锁
private static var lock = NSLock()
static func set(_ key: String, _ value: Any) {
lock.lock()
defer {
lock.unlock()
}
}
private static var lock = NSRecursiveLock()
static func set(_ key: String, _ value: Any) {
lock.lock()
defer {
lock.unlock()
}
}