Swift 语言编写静态Framework,引用Objective-C文件

1、将Objective-C头文件拖入framework工程引用,应可在以下路径看到头文件 Build Phases -> Headers -> Project 2、创建Bridge头文件,如 “TestBridging.h”,并填入头文件的相对路径 Build Settings -> Swift Compiler General -> Objective-C Bridging Header -> xxxx/TestBridging.h 3、在Bridge头文件中引用Objective-C头文件 (不用使用<>形式引用) // TestBridging.h #import "A.h" #import "B.h" #import "C.h" 4、关闭 defines Module Build Settings -> Packaging -> Defines Module -> No

August 14, 2023 · 1 min · holdsky

Swift 注解 @resultBuilder

使用SwiftUI时,经常会遇到@ViewBuilder,代码看起来很简洁,背后确实复杂的语法规则。 本文涉及三点: @resultBuilder 可变参数 闭包以及尾随闭包 我们可以自己定义一个类似@ViewBuilder的注解,本文将实现一个自定义的字符串拼接的注解。 首先 用 @resultBuilder 修饰的 struct符号需要实现静态 buildBlock 方法 @resultBuilder public struct TestStringBuilder { public static func buildBlock( s1 : String ,s2 : String) -> String { return s1 + s2 } } 然后定义一个函数,只有一个闭包参数,参数使用 @TestStringBuilder 修饰 func test_string_builder(@TestStringBuilder strs_join: () -> String ) { print(strs_join()) } 现在可以给 test_string_builder 传入闭包参数了 test_string_builder( strs_join :{ "a" "b" }) //将打印字符串 “ab” 该尾随闭包出场了,它是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用 test_string_builder(){ "a" "b" } //将打印字符串 “ab” 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,可以把()省略掉 test_string_builder{ "a" "b" } //将打印字符串 “ab” 现在是不是有点像@ViewBuilder的用法了?最后是可变参数,修改下 buildBlock 定义,以支持可变参数 ...

June 28, 2023 · 1 min · holdsky

iOS中集成Rust

本文假设已经在macOS机器上安装了xcode 本文参考 https://www.rust-lang.org/zh-CN/ macOS 中安装 rust开发环境 安装 rust 运行下面命令 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 中途会提示用户选择哪种安装方式:1、默认安装 ;2、自定义安装 ;3、取消安装 本文选择默认安装,这将会在 ~/.rustup 安装工具链,在 ~/.cargo 安装包管理器。 安装完成后,可以使用 rustc --version 查看版本 更新rust rust会定期更新(6周),可使用命令 rustup update 进行更新 安装苹果工具链 rustup target add 指定架构工具链名称 # 例如 rustup target add aarch64-apple-ios aarch64-apple-ios , 真机arm64 armv7-apple-ios , 真机armv7 armv7s-apple-ios , 真机armv7s x86_64-apple-ios, 模拟器x86_64 i386-apple-ios, 模拟器i386 安装VSCode 和Rust插件(本步骤可选) 建议使用VSCode + rust-analyzer扩展进行rust代码编写,详细参考https://code.visualstudio.com/docs/languages/rust 创建iOS可用的rust静态库 创建rust项目 cargo new 项目名字 --lib # 例如 cargo new ios_rust_hello --lib 上面命令会创建一些文件,基本结构如下 ...

June 19, 2023 · 1 min · holdsky

手动计算Git Commit ID(Git Hash)

关于 Git Commit ID的计算过程讨论,可参考 How is git commit sha1 formed 计算过程 # 获取元数据字符串 info=`git cat-file commit HEAD` # 计算元数据的字符串长度 infoLen=`echo $info | wc -c` infoLen=${infoLen// /} # 拼接字符串 str=`printf "commit %s\0" $infoLen` str=$str$info # 计算SHA1 echo $str | shasum 其他 使用 git cat-file -p hash 命令查看git hash代表的具体内容 git cat-file -p 哈希字符串

May 4, 2023 · 1 min · holdsky

file system sandbox blocked mmap()

不允许从沙盒目录加载动态库(代码),尤其是在生产环境上。 在iOS9中,还没有这么严格的管控要求,在iOS10+后要求更加严格了。 https://developer.apple.com/forums/thread/711730

May 4, 2023 · 1 min · holdsky

iOS运行时解析embedded.mobileprovison

embedded.mobileprovison是由二进制的加密数据和普通文本字符串组成,我们使用NSPropertyListSerialization类解析普通文本字符串 首先将普通文本字符串提前出来,这里用到了NSScanner NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]; NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:embeddedPath encoding:NSISOLatin1StringEncoding error:nil]; NSScanner *scanner = [[NSScanner alloc] initWithString:embeddedProvisioning]; if (![scanner scanUpToString:@"<plist" intoString:nil]) { NSLog(@"parse embedded.mobilerovision error, no plist markup"); return nil; } NSString *extractedPlist = nil; //接收普通文本字符串 if (![scanner scanUpToString:@"</plist>" intoString:&extractedPlist]) { NSLog(@"parse embedded.mobilerovision error, no plist markup"); return nil; } extractedPlist = [extractedPlist stringByAppendingString:@"</plist>"]; //添加xml标签结束标记 然后就可以使用NSPropertyListSerialization解析了 NSError *error; NSDictionary *plist = [NSPropertyListSerialization propertyListWithData:[extractedPlist dataUsingEncoding:NSISOLatin1StringEncoding] options:NSPropertyListImmutable format:nil error:&error]; if (error || ![plist isKindOfClass:[NSDictionary class]]) { NSLog(@"parse embedded.mobilerovision error, %@",error); return nil; } return plist; //返回结果 参考 https://www.process-one.net/blog/reading-ios-provisioning-profile-in-swift/

April 11, 2023 · 1 min · holdsky

iOS构建动态库时忽略undefined symbols导致的missing symbol called

iOS开发环境下,构建动态库时可能会出现undefined symbols,某些情况下我们选择让链接器(ld)忽略这些错误 # 传给 ld特定 Wl参数,来忽略undefined symbols -Wl,-U ,符号A, -U ,符号B, -U, 符号C ... #或者使用 Xlinker -Xlinker -U -Xlinker 符号A,-Xlinker -U -Xlinker 符号B,-Xlinker -U -Xlinker 符号C... 上面的参数告诉链接器(ld),未定义的符号在其他地方实现了。 然后,当我们在hostApp环境中加载这个动态库时,可以正常加载,但是运行到某些场景时,会因为missing symbol called崩溃。 崩溃的原因是上面忽略的undefined symbols没有被加载。 而undefined symbols没有被加载的原因是链接器(ld)会只想Dead Code Strips这个动作,因为我们在构建动态库时选择了忽略undefined symbols,就有可能(不是一定)导致这些undefined symbols变成Dead Code而被移除掉。 为解决这个问题,我们还需要像链接器(ld)传递一些参数,强制这些在其他地方实现的undefined symbols被链接到hostApp环境中 # 注意和上面提到的参数区别,-u和-U,小写的u表示强制加载,大小的U表示忽略 # 传给 ld特定 Wl参数,来强制链接undefined symbols -Wl,-u ,符号A, -u, 符号B, -u, 符号C ... #或者使用 Xlinker -Xlinker -u -Xlinker 符号A,-Xlinker -u -Xlinker 符号B,-Xlinker -u -Xlinker 符号C... 另外,需要注意的是,上面提到的参数,忽略undefined symbols参数是在构建动态库过程中使用;强制链接undefined symbols参数是在构建hostApp时使用。

April 10, 2023 · 1 min · holdsky

不使用cocoapods-art插件情况下与jfrog协作

目前,cocoapods-art还不能很好的适配苹果M系列芯片的macOS系统,故而探索出这条兜底的协作路径,可以不用使用cocoapods-art插件。 本文涉及相关命令 curl、 tar、 git、 pod(cocoapods) 原理 cocoapods-art的原理是先从jfrog下载全量的podspec索引,然后在本地创建git仓库存储这些索引,供cocoapods使用。 cocoapods-art插件操作的git仓库路径在 ~/.cocoapods/repos 和 ~/.cocoapods/repos-art 里面。 同理,我们可以按照cocoapods-art这个流程来实现我们自己的索引管理方案。 下载索引 这里使用artifactory提供rest api,将索引下载到 /AAAA(绝对路径,且不和操作系统用户绑定) 路径 # -u 用户名密码鉴权 # -A 自定义http的User-Agent curl -u artifactory用户名:密码 -f -L -o /AAAA/file.tgz http://artifactory域名/artifactory/api/pods/jfrog仓库名/index/fetchIndex --create-dirs --netrc-optional --retry 2 -A 'CocoaPods/1.11.3 cocoapods-downloader/1.6.3' --ssl-no-revoke 创建git仓库或者更新git仓库 第一次下载索引文件时的操作(创建添加) 索引文件下载到 /AAAA/file.tgz,使用 tar 命令解压 tar xf /AAAA/file.tgz -C /AAAA # 解压后删除压缩包 rm /AAAA/file.tgz 将 /AAAA 目录初始化为git仓库,再次下载索引时只要更新git仓库就可以了 # 第一次下载索引文件后,初始化git仓库 cd /AAAA git init git branch -M main #设置main为默认目录 # 添加索引文件到git仓库 git add . git commit -m "add message" # 再次下载所以文件后,直接讲索引添加到git仓库 再次下载索引文件时的操作(更新) 索引文件下载到 /AAAA/file.tgz,先删除旧索引(specs),再使用 tar 命令解压压缩包 ...

March 16, 2023 · 1 min · holdsky

Objective-C 调用 Swift : dyld missing symbol called

一个可能的原因:多个静态库(或动态库)的Minimum Deployments不一致,通常相差比较大。 例如 A库是Objective-C开放,最小依赖iOS 11; B库是swift开放,最小依赖iOS 16 而HostApp是iOS 11,这在运行时很可能触发 missing symbol called 猜测是iOS 11和iOS16的swift运行时库有较大差异。

February 13, 2023 · 1 min · holdsky

将iOS静态库转为动态库

本文指的转换为无源码时的转换(有源码那叫编译构建) 涉及工具 libtool 用于静态库转为动态库 lipo 用于查看库信息,将fat库拆分为thin库,将thin库合并为fat库 ar 用于将thin库解压为object文件 基本过程,将fat静态库拆分为thin静态库,再将thin静态库解压为object文件,然后将object文件压缩为thin动态库,最后将thin动态库合并为fat动态库 拆分fat静态库 先查看fat库基本信息,重点是看由哪些指令架构的thin库组成 lipo -info libfat.a # 输出信息中重点看指令架构 Architectures in the fat file: libfat.a are: x86_64 arm64 按照指令架构拆分fat库 lipo libfat.a -extract x86_64 -output libfoo.a 解压thin静态库 会输出一组 .o 文件 ar -x libfoo.a 压缩为thin动态库 注意下,需要手动添加依赖库, -framework 依赖库 -l依赖库 libtool -dynamic *.o -o libfoo_dynamic-x86_64.dylib -framework CoreFoundation -lSystem 将thin动态库合并为fat动态库 lipo -create libfoo_dynamic-x86_64.dylib libfoo_dynamic-arm64.dylib -output ./libfoo_dynamic.dylib 参考 https://pewpewthespells.com/blog/convert_static_to_dynamic.html

February 8, 2023 · 1 min · holdsky