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

Posted Deft_MKJing宓珂璟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 5.1 温故而知新笔记系列之第六天相关的知识,希望对你有一定的参考价值。

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]


以上是关于Swift 5.1 温故而知新笔记系列之第六天的主要内容,如果未能解决你的问题,请参考以下文章

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

python运维开发之第六天

零基础之第六天(is,id,编码,小数据池)

go系列-笔记(第六天)

swift学习第六天:数组

《Pro Android Graphics》读书笔记之第六节