post_install do |installer|
installer.generated_projects.each do |project|
project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
end
/App-exxigwycpikvlpgzpmccfjpmuvkx/Build/Intermediates.noindex/Pods.build/Release-iphonesimulator/YYCategories.build/Objects-normal/x86_64/Binary/YYCategories
ld: file not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a
clang: error: linker command failed with exit code 1 (use -v to see invocation)
** BUILD FAILED **
The following build commands failed:
Ld /Users/mengruirui/Library/Developer/Xcode/DerivedData/App-exxigwycpikvlpgzpmccfjpmuvkx/Build/Intermediates.noindex/Pods.build/Release-iphonesimulator/YYCategories.build/Objects-normal/x86_64/Binary/YYCategories normal x86_64 (in target 'YYCategories' from project 'Pods')
(1 failure)
注:不同语言的编译器对应的符号名的生成规则是不同的,C 语言只是在原函数名前加一个前缀“_”,如objc_retain(A),编译后符号名是_objc_retain,而 C++ 语言会根据方法名加上参数名生成的符号名,如__ZNSt3__16vectorIdNS_9allocatorIdEEEixB6v15006Em。
LLVM 17 will be released. As usual, I maintain lld/ELF and have addedsome notes to https://github.com/llvm/llvm-project/blob/release/17.x/lld/docs/ReleaseNotes.rst.Here I will elaborate on some changes.
When --threads= is not specified, the number ofconcurrency is now capped to 16. A large --thread= can harmperformance, especially with some system malloc implementations likeglibc's. (D147493)
--remap-inputs= and --remap-inputs-file=are added to remap input files. (D148859)
--lto= is now available to supportclang -funified-lto (D123805)
--lto-CGO[0-3] is now available to controlCodeGenOpt::Level independent of the LTO optimizationlevel. (D141970)
--check-dynamic-relocations= is now correct 32-bittargets when the addend is larger than 0x80000000. (D149347)
--print-memory-usage has been implemented for memoryregions. (D150644)
SHF_MERGE, --icf=, and--build-id=fast have switched to 64-bit xxh3. (D154813)
Quoted output section names can now be used in linker scripts.(#60496 <https://github.com/llvm/llvm-project/issues/60496>_)
MEMORY can now be used without a SECTIONScommand. (D145132)
REVERSE can now be used in input section descriptionsto reverse the order of input sections. (D145381)
Program header assignment can now be used withinOVERLAY. This functionality was accidentally lost in 2020.(D150445)
Operators ^ and ^= can now be used inlinker scripts.
LoongArch is now supported.
DT_AARCH64_MEMTAG_* dynamic tags are now supported. (D143769)
AArch32 port now supports BE-8 and BE-32 modes for big-endian. (D140201) (D140202) (D150870)
R_ARM_THM_ALU_ABS_G* relocations are now supported. (D153407)
.ARM.exidx sections may start at non-zero outputsection offset. (D148033)
Arm Cortex-M Security Extensions is now implemented. (D139092)
BTI landing pads are now added to PLT entries accessed by rangeextension thunks or relative vtables. (D148704) (D153264)
AArch64 short range thunk has been implemented to mitigate theperformance loss of a long range thunk. (D148701)
R_AVR_8_LO8/R_AVR_8_HI8/R_AVR_8_HLO8/R_AVR_LO8_LDI_GS/R_AVR_HI8_LDI_GShave been implemented. (D147100) (D147364)
--no-power10-stubs now works for PowerPC64.
DT_PPC64_OPT is now supported. (D150631)
PT_RISCV_ATTRIBUTES is added to include theSHT_RISCV_ATTRIBUTES section. (D152065)
R_RISCV_PLT32 is added to support C++ relative vtables.(D143115)
RISC-V global pointer relaxation has been implemented. Specify--relax-gp to enable the linker relaxation. (D143673)
The symbol value of foo is correctly handled when--wrap=foo and RISC-V linker relaxation are used. (D151768)
x86-64 large data sections are now placed away from code sections toalleviate relocation overflow pressure. (D150510)
When using glibc malloc with a largerstd::thread::hardware_concurrency (say, more than 16),parallel relocation scanning can be quite slower without the--threads=16 throttling.
I usually try to make extensions, unless too LLVM internal specific(e.g. --lto-*), accepted by the binutils community. The feature request for--remap-inputs= and --remap-inputs-file= was asuccess story, implemented by GNU ld 2.41.
PT_RISCV_ATTRIBUTES output is still not quite right. Ialso question about its usefulness. Unfortunately, at this stage, it'sdifficult to getrid of it.
This cycle has a surprising number of new features, and I have spentlots of spare time reviewing them to ensure that they are robust andproperly tested. Most stuff is completely unrelated to my day job.
There are quite a few AArch32 changes from Arm engineers, primarilyabout big-endian support and Cortex-M Security Extensions.
I was firm that the RISC-V global pointer relaxation needs to beopt-in. I had a GNU ld --relax-gp patch last year andutilitized this opportunity (ld.lld feature proposal) to move forwardGNU ld --relax-gp. It's unfortunately opt-out, but havingan option is a step forward.
This release adds support for LoongArch, which is a relatively newarchitecture that took inspiration from Mips and RISC-V.
Speed
Unlike previous versions, there is just a minor performanceimprovement compared with lld 15.0.0. I added a simplified version of64-bit xxh3 into the LLVMSupport library and utilized it inlld.
Linking a -DCMAKE_BUILD_TYPE=Debug build of clang 16:
1 2 3 4 5 6 7 8 9 10 11 12
% hyperfine --warmup 2 --min-runs 25 "numactl -C 20-27 "{/tmp/out/custom-16/bin/ld.lld,/tmp/out/custom-17/bin/ld.lld}" @response.txt --threads=8" Benchmark 1: numactl -C 20-27 /tmp/out/custom-16/bin/ld.lld @response.txt --threads=8 Time (mean ± σ): 3.159 s ± 0.035 s [User: 7.089 s, System: 3.076 s] Range (min … max): 3.095 s … 3.250 s 25 runs
Benchmark 2: numactl -C 20-27 /tmp/out/custom-17/bin/ld.lld @response.txt --threads=8 Time (mean ± σ): 3.131 s ± 0.027 s [User: 6.851 s, System: 3.101 s] Range (min … max): 3.080 s … 3.198 s 25 runs
Summary 'numactl -C 20-27 /tmp/out/custom-17/bin/ld.lld @response.txt --threads=8' ran 1.01 ± 0.01 times faster than 'numactl -C 20-27 /tmp/out/custom-16/bin/ld.lld @response.txt --threads=8'
This influence to the total link time is small. However, if I testthe time proportion of the hash function in the total link time, I cansee that the proportion has been reduced to nearly one third. On someworkload and some machines this effect may be larger.
其次,PCH 会引发命名空间被污染的问题,因为 PCH 引入的头文件会出现在你代码中的每一处,而这可能会是多于的操作,比如 iAd 应当出现在一些与广告相关的代码中,它完全没必要出现在帮助相关的代码中(也就是与广告无关的逻辑),可是当你把它放到 PCH 中,就意味组件里的所有地方都会引入 iAd 的代码,包括帮助页面,这可能并不是我们想要的结果!
Associates public, private, or project header files with the target. Public and private headers define API intended for use by other clients, and are copied into a product for installation. For example, public and private headers in a framework target are copied into Headers and PrivateHeaders subfolders within a product. Project headers define API used and built by a target, but not copied into a product. This phase can be used once per target.
Public: The interface is finalized and meant to be used by your product’s clients. A public header is included in the product as readable source code without restriction. Private: The interface isn’t intended for your clients or it’s in early stages of development. A private header is included in the product, but it’s marked “private”. Thus the symbols are visible to all clients, but clients should understand that they’re not supposed to use them. Project: The interface is for use only by implementation files in the current project. A project header is not included in the target, except in object code. The symbols are not visible to clients at all, only to you.
我们可以看到,private_header_files 在这里的含义是说,它本身是相对于 public 而言的,这些头文件本义是不希望暴露给用户使用的,而且也不会产生相关文档,但是在构建的时候,会出现在最终产物中,只有既没有被 public 和 private 标注的头文件,才会被认为是真正的私有头文件,且不出现在最终的产物里。
其实这么看来,CocoaPods 对于 public 和 private 的理解是和 Xcode 中的描述一致的,两处的 Private 并非我们通常理解的 Private,它的本意更应该是开发者准备对外开放,但又没完全 ready 的头文件,更像一个 In Progress 的含义。
Associates public, private, or project header files with the target. Public and private headers define API intended for use by other clients, and are copied into a product for installation. For example, public and private headers in a framework target are copied into Headers and PrivateHeaders subfolders within a product. Project headers define API used and built by a target, but not copied into a product. This phase can be used once per target.
Public: The interface is finalized and meant to be used by your product’s clients. A public header is included in the product as readable source code without restriction. Private: The interface isn’t intended for your clients or it’s in early stages of development. A private header is included in the product, but it’s marked “private”. Thus the symbols are visible to all clients, but clients should understand that they’re not supposed to use them. Project: The interface is for use only by implementation files in the current project. A project header is not included in the target, except in object code. The symbols are not visible to clients at all, only to you.
我们可以看到,private_header_files 在这里的含义是说,它本身是相对于 public 而言的,这些头文件本义是不希望暴露给用户使用的,而且也不会产生相关文档,但是在构建的时候,会出现在最终产物中,只有既没有被 public 和 private 标注的头文件,才会被认为是真正的私有头文件,且不出现在最终的产物里。
其实看起来,CocoaPods 对于 public 和 private 的官方解释是和 Xcode 中的描述一致的,两处的 Private 并非我们通常理解的 Private,它的本意更应该是开发者准备对外开放,但又没完全 Ready 的头文件,更像一个 In Progress 的含义。
// this is part of Gemfile source 'http://sakgems.sankuai.com/' do gem 'cocoapods-hmap-prebuilt' gem 'XXX' ... end // this is part of Podfile target 'XXX' do plugin 'cocoapods-hmap-prebuilt' pod 'XXX' ... end
Clang 导入器(Clang Importer):Clang 导入器(在 lib/ClangImporter 中实现)负责导入 Clang 模块,并将导出的 C 或 Objective-C API 映射到相应的 Swift API 中。最终导入的 AST 可以被语义分析引用。
SIL 生成(SIL Generation):Swift 中间语言(Swift Intermediate Language,SIL)是一门高级且专用于 Swift 的中间语言,适用于对 Swift 代码的进一步分析和优化。SIL 生成阶段(在 lib/SILGen 中实现)将经过类型检查的 AST 弱化为所谓的「原始」SIL。SIL 的设计在 docs/SIL.rst 有所描述。
SIL 保证转换(SIL Guaranteed Transformations):SIL 保证转换阶段(在 lib/SILOptimizer/Mandatory 中实现)负责执行额外且影响程序正确性的数据流诊断(比如使用未初始化的变量)。这些转换的最终结果是「规范」SIL。
SIL 优化(SIL Optimizations):SIL 优化阶段(在 lib/Analysis、lib/ARC、lib/LoopTransforms 以及 lib/Transforms 中实现)负责对程序执行额外的高级且专用于 Swift 的优化,包括(例如)自动引用计数优化、去虚拟化、以及通用的专业化。
LLVM IR 生成(LLVM IR Generation):IR 生成阶段(在 lib/IRGen 中实现)将 SIL 弱化为 LLVM LR,此时 LLVM 可以继续优化并生成机器码。
从原文的内容里,我们可以看到 Swift 编译器主要是在前端部分增加了一些环节,主要是在语义分析和中间代码生成的过程中增加了几个步骤:
var modelObject: ModelObject!
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
modelObject.valueObservable.map { possibleValue -> String in
if let value = possibleValue {
return "Selected value is: \(value)"
} else {
return "No value selected"
}
}.bind(to: self.textLabel.rx.text).disposed(by: disposeBag)
}
class MyObject {
var b: Int? = 2
var a: Int? = 3
}
var o: MyObject? = .init()
bytes(of: o) // [160, 142, 188, 2, 0, 96, 0, 0]
o = nil
bytes(of: o) // [0, 0, 0, 0, 0, 0, 0, 0]
无论 Class 中有多少成员变量,Class 变量本身(即指向它的指针)只占 8 字节(64位系统中)。
Enum
枚举类型一般是有限的,最终总可以找到一个不在枚举范围内的值表示 没有值,也可以没有内存浪费。
enum Edge {
case left
case right
case top
case bottom
}
var e: Edge? = .left
bytes(of: e) // [0]
e = .bottom
bytes(of: e) // [3]
e = nil
bytes(of: e) // [4],用越界值表示 nil,没有值
当然并不是所有 Enum 类型都能这样,带关联值的就可能不行。
结语
综上所述,Swift 编译器会尽可能地优化可选值的内存占用,日常开发并不需要太多关心,但是部分情况仍要求开发者尽量少使用可选值,如结构体中连续几个可选 Int 的情况,如果 0 也能满足代码逻辑,就使用非可选值,并用 0 初始化它吧!
// 浪费的内存比较可观
struct My {
var a: Int?
var b: Int?
var c: Int?
var d: Int?
}
相信很多单纯objc语言开发的程序员在平日的开发中很少会用到桥式转换,那么为什么需要桥式转换呢?什么场景下我们会用到桥式转换呢? 其实在 Foundation 框架中,objc类所具备的某些功能,是 CoreFoundation 框架中的C语言数据结构所不具备的,反之亦然。举个例子,假如我们在使用objc的字典NSDictionary时,如果我们想key存入的是一个我们自定义的模型对象,那就会出问题,例如下面的代码
为什么会出现在 -[MyCustomClass copyWithZone:]: unrecognized selector sent to instance 0x6000005a43e0 这个错误呢?这是因为在 Foundation 框架中的字典,其键的内存管理语义为 “拷贝”,而值的语义是却是“保留”,而NextModel并没有遵循NSCopying协议,并实现copyWithZone方法,所以产生了崩溃。但是CoreFoundation 框架中的字典定义是可以自定义其内存管理语义 的,这时候我们就可以先定义CoreFoundation 框架中的字典,然后使用强大的无缝桥接技术,将它转换成 Foundation 框架中的字典,就能解决这个问题了。 以下就来说一下如何构造CoreFoundation的字典,先来看看苹果的定义文档如下:
当我以为这样已经结束的时候,我把我的代码运行了一下,结果还是报了 -[MyCustomClass copyWithZone:]: unrecognized selector sent to instance 0x600003cd42b0 的错误,无论怎么改都无补于事,难道网上的资料是错误的吗?我尝试询问ChatGpt,得到以下的回答
layer的内容(包括子layer)必须是静态的,因为一旦发生变化(如resize,动画),之前辛苦处理得到的缓存就失效了。如果这件事频繁发生,我们就又回到了“每一帧都需要离屏渲染”的情景,而这正是开发者需要极力避免的。针对这种情况,Xcode提供了“Color Hits Green and Misses Red”的选项,帮助我们查看缓存的使用是否符合预期
def findProfileResources(path):
pathDir = os.listdir(path)
for allDir in pathDir:
child = os.path.join('%s%s' % (path, allDir))
if os.path.isfile(child):
# 获取读到的文件的后缀
end = os.path.splitext(child)[-1]
if end != ".dylib" and end != ".car" and end != ".png" and end != ".webp" and end != ".gif" and end != ".js" and end != ".css":
print(child + " 后缀 " + end)
else:
# 递归遍历子目录
child = child + "/"
findProfileResources(child)
function createStore(reducer) {
var state;
var listeners = []
function getState() {
return state
}
function subscribe(listener) {
listeners.push(listener)
return function unsubscribe() {
var index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
}
function dispatch(action) {
state = reducer(state, action)
listeners.forEach(listener => listener())
}
dispatch({})
return { dispatch, subscribe, getState }
}
class Car {
init() {
print("car has init")
}
func run() {
print("car running")
}
}
class Person {
lazy var car = Car.init()
init() {
print("person has init")
}
func go_out() {
car.run()
}
}
let p = Person.init() //person has init
print("*******")
p.go_out()// car has init ----> car running
当结构体 包含 一个延迟存储属性时,只有 var 才能访问延迟 存储属性
因为延迟属性 初始化时 要改变 结构体的内存
struct Point {
var x = 0
var y = 0
lazy var z = 0
}
let p = Point.init()
print(p.z)//Cannot use mutating getter on immutable value: 'p' is a 'let' constant
什么是 类型 属性?
类型属性(Type Property) :通过类型去访问
存储类型属性(Stored Type Property):整个程序运行过程中,就只有1份内存(类似于全局变量,底层采用了 gcd_once 操作)
// 方式一
public class SingleManager{
public static let shared = SingleManager()
private init(){}
}
// 方式二
public class SingleManager{
public static let shared = {
//...
//...
return SingleManager()
}()
private init(){}
}
// 上述两个方法等价,一般推荐 方式二
Swift 中 下标是什么?
使用subscript 可以给任意类型(枚举,结构体,类) 增加下标的功能
subscript 的语法 类似于 实例方法、计算属性,本质就是方法(函数)
func xiabiaoTest() {
class Point {
var x = 0.0
var y = 0.0
subscript (index:Int) -> Double {
set{
if index == 0 {
x = newValue
} else if index == 1 {
y = newValue
}
}
get{
if index == 0 {
return x
} else if index == 1 {
return y
}
return 0
}
}
}
let p = Point()
p[0] = 11.1 // 调用subscript
p[1] = 22.2 // 调用subscript
print(p.x)//11.1 不会调用 subscript
print(p.y)//22/2 不会调用 subscript
print(p[0])//11.1 // 调用subscript
print(p[1])//22.2 // 调用subscript
}
class Car { var price = 0}
class Dog { var weight = 0}
class Person {
var name:String = ""
var dog :Dog = Dog()
var car :Car? = Car()
func age() -> Int { 18 }
func eat() {print("Person Eat")}
subscript (index:Int) ->Int {index}
}
var person :Person? = Person()
var age1 = person!.age() //Int
var age2 = person?.age() // Int?
var name = person?.name // String?
var index = person?[6] // Int?
func getName() -> String {"jackie"}
/*
如果 person 对象 是 nil ,将不会调用 getName() 方法
*/
person?.name = getName()
如果结果本来就是可选项,不会进行再次包装
if let _ = person?.eat() {
/*
Person Eat
eat success
*/
print("eat success")
} else {
print("eat failure")
}
多个 ? 可以连接在一起,其中任何一个节点 如果为 nil,那么整条链就会 调用失败
var dog = person?.dog // Dog?
var weight = person?.dog.weight // Int?
var price = person?.car?.price // Int?
什么是运算符重载?
类、结构体、枚举可以为现有的运算符提供自定义的实现,这个操作叫做:运算符重载
struct Point {
var x: Int,y: Int
static func + (p1: Point,p2: Point) -> Point {
Point(x: p1.x + p2.x ,y: p1.y + p2.y)
}
}
let p = Point(x:10,y: 20) + Point(x: 30,y: 40)
print(p) //Point(x: 40, y: 60)
func curring_fun_two_params2<A,B,C>(_ f1: @escaping (A,B) -> C) -> (A) -> ((B) -> C) {
/*
return {
(a) in // a = 3
return {
(b) in // b = 8
return f1(a,b)
}
}
*/
{ a in { b in f1(a, b)} }
}
let result = curring_fun_two_params2(add)(3)(5)
print("result == ",result) //8
柯里化拓展
/*
-> (A) -> (B) -> (C) -> (D) -> E
实际是 一连串 闭包的 组合 如下所示:
-> (A) -> ( (B) -> ((C) -> ((D) -> E)) )
传入 A > 一个 闭包 (B) -> ( (C) -> ((D) -> E) )
传入 B >> 一个 闭包 (C) -> ( (D) -> E )
传入 C >> 一个闭包 (D) -> E
*/
//func curring_fun_more_params<A,B,C,D,E>(_ fn: @escaping (A,B,C,D) -> (E)) -> (A) -> ((B) -> ((C) -> ((D) -> E))) {
func curring_fun_more_params<A,B,C,D,E>(_ fn: @escaping (A,B,C,D) -> (E)) -> (A) -> (B) -> (C) -> (D) -> E {
/*
return {
(a) in
return {
(b) in
return {
(c) in
return {
d in
return fn(a,b,c,d)
}
}
}
}
*/
{a in { b in { c in { d in fn(a,b,c,d)}}}}
}
let resutl2 = curring_fun_more_params(add2)(10)(20)(30)(40)
print(resutl2) // 40
let resutl2_func = curring_fun_more_params(add2)
let resutl2_func_value = resutl2_func(10)(20)(30)(40)
print(resutl2_func_value) // 40