Swift 进度 swift进阶

admin2024-08-22  9

String

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的内容,编译器会自动做优化

Swift 进度 swift进阶,Swift 进度 swift进阶_ide,第1张

for c in "jack" { // c是Character类型 print(c) }

var str = "jack"
var c = str[str.startIndex] // c是Character类型
String相关的协议

BidirectionalCollection

startIndex协议包含的部分内容

  • endIndexindex属性、String方法
  • ArrayRangeReplaceableCollection都遵守了这个协议

append协议包含的部分内容

  • insertremoveString方法
  • ArrayDictionary都遵守了这个协议

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

NSStringString之间可以随时随地的桥接转换

如果你觉得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比较字符串内容是否等价的转换会调用函数来实现的,相对会有性能的消耗,但由于编译器的优化,消耗的成本可以忽略不计

Swift 进度 swift进阶,Swift 进度 swift进阶_字符串_02,第2张

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的几个类型的转换表格方法

Swift 进度 swift进阶,Swift 进度 swift进阶_运算符_03,第3张

Swift 进度 swift进阶,Swift 进度 swift进阶_Swift 进度_04,第4张

Swift 进度 swift进阶,Swift 进度 swift进阶_运算符_05,第5张

String

NSStringNSMutableString可以相互转换,而String就只能单向转换成

只能被class继承的协议

其他类型同理

Swift 进度 swift进阶,Swift 进度 swift进阶_运算符_06,第6张

AnyObject、class、@objc

如果协议对应@objc来修饰,那么只能被类所遵守

Swift 进度 swift进阶,Swift 进度 swift进阶_字符串_07,第7张

// 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

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不可以增加存储属性

借助关联对象,可以实现类似classclass 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()
    }
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!