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")
}
}
Swift
暴露给OC
的类必须继承子NSObject
- 使用
@objc
修饰需要暴露给OC
的成员- 使用
@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)
String
和NSString
可以随时桥接转换,如果觉得哪边的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
实现map
和filter
// 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 温故而知新笔记系列之第六天的主要内容,如果未能解决你的问题,请参考以下文章