飞道的博客

Swift 5.1 温故而知新笔记系列之第六天

250人阅读  评论(0)

1.Swift调用OC

Swift项目创建OC对象如下

NS_ASSUME_NONNULL_BEGIN

int sum(int a, int b);

@interface Person : NSObject

@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;

- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name;

- (void)run;
+ (void)run;

- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;

@end


int sum(int a, int b){
   
   return a + b;
}


@implementation Person

- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name{
   
   self = [super init];
   if (self) {
   
       _name = name;
       _age = age;
   }
   return self;
}

+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name{
   
   return nil;
}

+ (void)run{
   
   NSLog(@"Person +run");
}

- (void)run{
   
   NSLog(@"Person -run %@, %@",@(_age), _name);
}


+ (void)eat:(NSString *)food other:(NSString *)other{
   
   NSLog(@"Person +eat %@ %@",food, other);
}


- (void)eat:(NSString *)food other:(NSString *)other{
   
   NSLog(@"Person -eat %@ %@ %@ %@",@(_age), _name,food, other);
}

@end

一般都会自动创建一个Target-Bridging-Header.h的桥接文件,比如在Swift工程里面,需要调用OC的类,在桥接文件里面写上#import "Person.h"即可调用。

1.1 基本使用

var str: String = "Kejing"
// Swift 
var p = Person(age: 20, name: str);
print(p.name, p.age)

p.run()
p.eat("吃饭", other: "吃肉")

Person.run()
Person.eat("不吃", other: "Kj")


print(sum(20, 30))

1.2 C函数冲突

func sum(_ a: Int, _ b: Int) -> Int {
   
   a - b
}
print(sum(20, 30))

我们在swift中的有个同名的方法,就会覆盖import进来的C函数。

func sum(_ a: Int, _ b: Int) -> Int {
   
    a - b
}

@_silgen_name("sum")
func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32

print(swift_sum(20, 30))
print(sum(20, 30))

可以用_silgen_name的方式进行重命名,那么调用原来的swift函数就可以不变,调用import进来的C函数就可以用重命名的方式调用。

@_silgen_name后面跟的参数只要这个C/C++函数存在,就可以重命名,比如系统分配内存的私有方法swift_allocObject,理论上也可以采用这种方式重命名,然后就可以调用系统级别的私有方法了。

2. OC调用Swift

Xcode已经默认生成了一个OC调用Swift的头文件,格式targetname-Swift.h

根据上面Swift调用OC的代码,都介绍了,新增一个C方法testMethod给Swift调用,然后OC里面再调用Swift的Car类

import Foundation

@objcMembers class Car: NSObject {
   
    var price: Double
    var brand: String
    init(price: Double, brand: String) {
   
        self.price = price
        self.brand = brand
    }
    
    func run() -> Void {
   
        print(price, brand, "run")
    }
    static func run() {
   
        print("Car run")
    }
}

extension Car {
   
    func test() {
   
        print(price, brand, "test")
    }
}
  1. Swift暴露给OC的类必须继承子NSObject
  2. 使用@objc修饰需要暴露给OC的成员
  3. 使用@objcMembers修饰类,代表所有成员都会暴露给OC,包括扩展

标记好之后,Person.m类中,导入头文件#import "TestSwift-Swift.h",然后看下头文件生成的Car

SWIFT_CLASS("_TtC9TestSwift3Car")
@interface Car : NSObject
@property (nonatomic) double price;
@property (nonatomic, copy) NSString * _Nonnull brand;
- (nonnull instancetype)initWithPrice:(double)price brand:(NSString * _Nonnull)brand OBJC_DESIGNATED_INITIALIZER;
- (void)run;
+ (void)run;
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end


@interface Car (SWIFT_EXTENSION(TestSwift))
- (void)test;
@end

在OC的类中调用代码如下

void testMethod(){
   
    Car *car = [[Car alloc] initWithPrice:42 brand:@"BMW"];
    [car run];
    [car test];
    [Car run];
}

3.灵魂拷问

1. 为什么Swift暴露给OC的类最终要继承自NSObject

Car *car = [[Car alloc] initWithPrice:42 brand:@"BMW"];

因为调用都是需要alloc,这个方法就是NSObject才有的,而且方法都是走objc_msgSend的,因此,都必须继承自NSObject

2. OC暴露给Swift调用底层走的是什么?

从某方面来理解,OC写的类,无论自己调用还是给Swift调都是走Runtime那一套,.m文件编译的时候就决定了,另一方面,看下汇编就知道了

3. 暴露给OC调用的Swift类自己调用底层走的什么?

上面可以看到,暴露给OC调用的的Swift类,在OC类调用都是alloc无用质疑都是走runtime那一套,但是虽然暴露给OC了,继承自NSObject,如果调用还是用Swift的调用方式,这种方式底层走的就是默认的虚表方式。

4. String使用

4.1 插入和删除

var emptyStr1 = ""
var emptyStr2 = String()
var str = "123456" print(str.hasPrefix("123")) // true print(str.hasSuffix("456")) // true
 var str: String = "1" // 拼接,jack_rose str.append("_2")
// 重载运算符 +
str = str + "_3" // 重载运算符 += str += "_4"
// \()插值
str = "\(str)_5"
// 长度,9,1_2_3_4_5 print(str.count)


 var str = "1_2"
// 1_2_
str.insert("_", at: str.endIndex)
// 1_2_3_4
str.insert(contentsOf: "3_4", at: str.endIndex)
// 1666_2_3_4
str.insert(contentsOf: "666", at: str.index(after: str.startIndex))
// 1666_2_3_8884
str.insert(contentsOf: "888", at: str.index(before: str.endIndex))
// 1666hello_2_3_8884
str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))
 // 666hello_2_3_8884
str.remove(at: str.firstIndex(of: "1")!)
// hello_2_3_8884
str.removeAll {
    $0 == "6" }
var range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex) // hello_2_3_4
str.removeSubrange(range)

4.2 SubString

 var str = "1_2_3_4_5"
// 1_2
var substr1 = str.prefix(3)
// 4_5
var substr2 = str.suffix(3)
// 1_2
var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3) var substr3 = str[range]
// 最初的String,1_2_3_4_5 print(substr3.base)
// Substring -> String
var str2 = String(substr3)

Substring和它的base,共享字符串数据
Substring发生修改 或者 转为String时,会分配新的内存存储字符串数据

4.3 String 和 NString

 var str1: String = "jack"
var str2: NSString = "rose"
var str3 = str1 as NSString
var str4 = str2 as String
// ja
var str5 = str3.substring(with: NSRange(location: 0, length: 2)) print(str5)


StringNSString可以随时桥接转换,如果觉得哪边的API不好用,可以转过去用

5. 多线程

5.1 异步

public typealias Task = () -> Void

public func async(_ task: @escaping Task) {
   
    _async(task)
}

public func async(_ task: @escaping Task, _ mainTask: @escaping Task){
   
    _async(task, mainTask)
}

private func _async(_ task: @escaping Task, _ mainTask: Task? = nil){
   
    let workTask = DispatchWorkItem(block: task)
    DispatchQueue.global().async(execute: workTask)
    if let main = mainTask {
   
        workTask.notify(queue: DispatchQueue.main, execute: main)
    }
}
 

5.2 延迟

public typealias Task = () -> Void
@discardableResult
public func asyncDelay(_ seconds: Double,
                       _ task: @escaping Task) -> DispatchWorkItem {
   
    return _asyncDelay(seconds, task)
}
@discardableResult
public func asyncDelay(_ seconds: Double,
                       _ task: @escaping Task,
                       _ mainTask: @escaping Task) -> DispatchWorkItem {
    return _asyncDelay(seconds, task, mainTask)
}
private 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
    
}

5.3 Once

dispatch_once已经被swift中废物,可以用 类型静态属性或者全局常量变量代替,默认断点bt都是lazy + dispatch_once的效果

fileprivate let initTask1: Void = {
   
   print("init task1")
}()

class Person{
   
    static let initTask2: Void = {
   
       print("init task2")
    }()
    
    static func test() -> Void {
   
        Self.initTask2
    }
}

5.4 加锁

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
    }
}
// Foundation
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() }
}

6. Array使用

6.1 Map,Filter,Reduce

// map
var arr = [1,2,3,4]

//var arr2 = arr.map { (element) -> Int in
//    element * 2
//}
var arr2 = arr.map {
    $0 * 2 }
// [2, 4, 6, 8]
print(arr2)

// filter
//var arr3 = arr.filter { (element) -> Bool in
//    element % 2 == 0
//}
var arr3 = arr.filter {
    $0 % 2 == 0 }
print(arr3)

// reduce
//var arr4 = arr.reduce(10) { (relust, element) -> Int in
//    return relust + element
//}

var arr4 = arr.reduce(10) {
    $0 + $1 }

print(arr4)

首先这边的$0代表的是闭包函数的参数,同理$1就是第二个参数,Map就是遍历数组,映射成新的数组,Filter就是根据返回值过滤,Reduce中有两个参数,第一个参数就是上一个结果的值,第二个就是遍历出来的值,而且有个初始化值。下面用Recude实现mapfilter

// map
var arr = [1,2,3,4]

print(arr.map{
    $0*2 })

//public func reduce<Result>(_ initialResult: Result,
//                           _ nextPartialResult: (Result, Element) -> Result)
//                            -> Result
print(arr.reduce([]) {
    (result, element) -> Array<Int> in
    result + [ 2 * element ]
})

print(arr.reduce([], {
    $0 + [ 2*$1 ] }))



// filter
print(arr.filter {
    $0%2 == 0 })

print(arr.reduce([]) {
    (result, element) -> Array<Int> in
    element % 2 == 0 ? result + [element] : result
})

print(arr.reduce([]) {
    $1 % 2 == 0 ? $0 + [$1] : $0 })

6.2 flatMap,compactMap

var arr = [1,2,3,4]


var arr1 = arr.map {
    Array.init(repeating: $0, count: $0) }

var arr2 = arr.flatMap {
    Array.init(repeating: $0, count: $0) }
// [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
print(arr1)

// [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
print(arr2)


var arr3 = ["123", "test", "mikejing", "-3213"]

// [Optional(123), nil, nil, Optional(-3213)]
var arr4 = arr3.map {
    Int($0) }
print(arr4)

// [123, -3213]
var arr5 = arr3.compactMap{
    Int($0) }
print(arr5)

6.3 lazy优化

正常的map会在调用之前就遍历一遍

var arr = [1,2,3,4]

let resut = arr.lazy.map {
    (element) -> Int in
    print("map\(element)")
    return element * 2
}

print("start----")
print("mapped", resut[0])
print("mapped", resut[1])
print("mapped", resut[2])
print("end----")

//start----
//map1
//mapped 2
//map2
//mapped 4
//map3
//mapped 6
//end----

6.4 OptionalmapflatMap

var num1: Optional<Int> = .some(10)

// Optional(20)
var num2 = num1.map{
    $0 * 2 }

var num3: Int? = nil
// nil
var num4 = num3.map{
    $0 * 2 }


var num5: Int? = 10

var num6 = num5.map{
    Optional.some($0 * 2) }

var num7 = num5.flatMap{
    Optional.some($0 * 2) }

// Optional(Optional(20))
print(num6)

// Optional(20)
print(num7)

有了map在可选项中,以下是等价的

var num1: Optional<Int> = .some(10)

// Optional(20)
var num2 = num1.map{
    $0 * 2 }

// Optional(20)
var num3 = (num1 != nil) ? (num1! * 2) : nil

可以看到,之前用let num的方式进行可选项绑定,现在也可以用map,更加的优雅,而且flatMap是可以消除多个可选项嵌套的。

// 案例一
var fmt1 = DateFormatter()

fmt1.dateFormat = "yyyy-MM-ddd"

var str: String? = "2021-11-02"

// old
var date1 = str != nil ? fmt1.date(from: str!) : nil

// new
print(str.flatMap{
    fmt1.date(from: $0) })


// 案例二
var score: Int? = 90

var str1 = score != nil ? "Score \(score)" : "No Score"

var str2 = score.map{
    "score \($0)" } ?? "No Score"

对可选项做map,如果不是空,会自动解包,如果是nil,直接返回

实际案例一

struct Person {
   
    var name: String
    var age: Int
}

var items = [
    Person(name: "qishu", age: 3),
    Person(name: "jiaojiao", age: 28),
    Person(name: "kejing", age: 30)
]

func getPerson1(_ name: String) -> Person? {
   
    let index = items.firstIndex {
     $0.name == name  }
    return index != nil ? items[index!] : nil
}

func getPerson2(_ name: String) -> Person? {
   
    items.firstIndex {
    $0.name == name }.map {
    items[$0] }
}

实际案例二

struct Person {
   
    var name: String
    var age: Int
    init?(_ json: [String : Any]) {
   
        guard let name = json["name"] as? String,
            let age = json["age"] as? Int else{
   
                return nil
        }
        self.name = name
        self.age = age
    }
}

var json: Dictionary? = ["name": "珂璟", "age":30]


// Optional(TestSwift.Person(name: "珂璟", age: 30))
var p1 = json != nil ? Person(json!) : nil


// Optional(TestSwift.Person(name: "珂璟", age: 30))
var p2 = json.flatMap {
    Person($0) }

if let p = p2{
   
    print(p.name, p.age)
}else{
   
    print("不存在")
}

转载:https://blog.csdn.net/Deft_MKJing/article/details/116403111
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场