iOS 动态库运行时符号冲突

本文demo 构造了这样的场景: 有两个动态库framework,DA和DB,DA定义了函数func,DB定义了函数func,两个func的名字一样,但是内部实现不一样,然后使用App加载这两个动态库,结果运行时func的调用和预期的不一致。 动态库DA // func.h const char * func(void); const char * func_a(void); // func.c #include "func.h" const char * func(void) { return "i m da func\n"; } const char * func_a(void) { return "i m da func_a\n"; } 动态库DB // func.h const char * func(void); const char * func_b(void); // func.c #include "func.h" const char * func(void) { return "i m db func\n"; } const char * func_a(void) { return "i m db func_b\n"; } App 集成 //Appdelegate.m #import "AppDelegate.h" #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { printf("%s",func()); //预期打印 i m da func printf("%s",func_a()); return YES; } //ViewController.m #import "ViewController.h" #import @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; printf("%s",func());//预期打印 i m db func printf("%s",func_b()); } @end 实测 ...

December 15, 2021 · 1 min · holdsky

iOS App页面置灰实现

App页面置灰,本质是将彩色图像转换为灰度图像,本文提供两种方法实现,一种是App整体置灰,一种是单个页面置灰,可结合具体的业务场景使用。 方法一:分别将图片和文字置灰 一般情况下,App页面的颜色深度是24bit,也就是RGB各8bit;如果算上Alpha通道的话就是32bit,RGBA(或者ARGB)各8bit。灰度图像的颜色深度是8bit,这8bit表示的颜色不是彩色,而是256种不同亮度的黑色或白色。 说到灰度图像,在YUV颜色空间上—其中Y代表亮度,调整Y值就可以得到不同的灰度图像。 理论上,颜色空间RGB和YUV是等价的,同一种颜色用RGB或YUV都可以表示。从RGB数值对应到亮度Y,一般采用公式Y = 0.299R+0.587G+0.114B,得到的结果再填充到RGB上就得到了对应的灰度RGB颜色。 Y = 0.299R+0.587G+0.114B Gray = RGB(Y,Y,Y) 以上是方法一App页面置灰的原理基础。 UIImage转成灰度图 核心是创建一个灰度空间,然后将图像绘制到这个空间上 -(UIImage*)getGrayImage:(UIImage*)sourceImage { int width = sourceImage.size.width; int height = sourceImage.size.height; // 创建灰度空间 CGColorSpaceRef colorSpace =CGColorSpaceCreateDeviceGray(); // 创建绘制上下文 CGContextRef context =CGBitmapContextCreate(nil,width,height,8,0,colorSpace,kCGImageAlphaNone); CGColorSpaceRelease(colorSpace); if(context== NULL){ return nil; } // 绘制原始图像到新的上下文(灰度) CGContextDrawImage(context,CGRectMake(0,0, width, height), sourceImage.CGImage); // 获取灰度图像 CGImageRef grayImageRef =CGBitmapContextCreateImage(context); // CGImage -> UIImage UIImage*grayImage=[UIImage imageWithCGImage:grayImageRef]; //回收资源 CGContextRelease(context); CGImageRelease(grayImageRef); return grayImage; } UIColor转成灰度颜色 比较简单了,使用公式就可以了 Y = 0.299R+0.587G+0.114B ...

December 14, 2021 · 1 min · holdsky

GoogleWebRTC for iOS 设置SDP和ICE顺序错误导致不互通

环境: GoogleWebRTC 1.1.31999 Xcode 13 iOS 15 问题描述: 1、 学习WebRTC时,先用simple-peer实现了浏览器和浏览器之间的音视频互通。 2、然后结合浏览器的经验,基于GoogleWebRTC for iOS实现了iOS真机和浏览器直接的互通 3、 最后在测试iOS真机之间的互通时,基本不互通,测试失败。排查了很长时间,最后定位为API调用顺序错误 应先设置远端SDP,再设置远端ICE,具体到API就是,先设置 调用 setRemoteDescription,然后再调用 addIceCandidate // 收到远端SDP和ICE信息后 [peerConnection setRemoteDescription:remoteSDP completionHandler:^(NSError * err){ [peerConnection addIceCandidate:remoteICE]; } ];

November 18, 2021 · 1 min · holdsky

iOS 从设备日志分析App网络性能

环境准备 Apple Console (控制台程序,MacOS自带) iPhone(iPad等设备也可,不需要越狱) 数据线 iOS App(不需要破解,直接从AppStore安装就可以) 操作步骤 1、通过usb将iPhone手机和Mac电脑连接 2、打开 Apple Console,选中的设备 3、点击开始,可以实时看到设备日志了,然后打开App,运行一段时间。 在日志里面搜索关键字 summary for task,应可以看到对网络性能的统计 字段解析 先摘抄一段日志 16:34:02.523191+0800 MPBBank Task <4C18F624-05B7-427D-AC9D-46C6C69B29C9>.<1> summary for task success {transaction_duration_ms=576, response_status=200, connection=1, protocol="http/1.1", domain_lookup_duration_ms=105, connect_duration_ms=240, secure_connection_duration_ms=167, private_relay=false, request_start_ms=389, request_duration_ms=0, response_start_ms=574, response_duration_ms=1, request_bytes=1629, response_bytes=10056, cache_hit=false} 以上对应 日志时间(16:34:02.523191+0800), 日志发生源的进程(MPBBank,此为招商银行App的进程), 日志信息(Task <6510978A-4EA6-4970-A5A6-1AFBF3B0C0E1>.<1> ….) 主要关注下 日志信息 字段,可以看到其内容为 Task <4C18F624-05B7-427D-AC9D-46C6C69B29C9>.<1> # 任务Id summary for task success # 任务成功统计 { transaction_duration_ms=576, # 传输时长 response_status=200, # 响应码 connection=1, protocol="http/1.1", # 网络协议 domain_lookup_duration_ms=105, # DNS查询时长 connect_duration_ms=240, # 连接时长secure_connection_duration_ms=167, # 安全连接时长 private_relay=false, request_start_ms=389, # 请求开始时刻 request_duration_ms=0, # 请求时长 response_start_ms=574, # 响应开始时刻 response_duration_ms=1, # 响应时长 request_bytes=1629, # 请求报文大小 response_bytes=10056, # 响应报文大小 cache_hit=false # 是否命中缓存 } 基本上从字面意思解释就可以了。 经过反复观察,以上部分数据存在以下关系: transaction_duration_ms = response_start_ms + response_duration_ms,最终计算结果可能会有几毫秒误差。 ...

November 11, 2021 · 1 min · holdsky

iOS WKWebView 播放视频时禁用自动全屏播放

关键属性 webkit-playsinline、 playsinline、 allowsInlineMediaPlayback 需要在html style里面和native 里面同时设置才有效 html video增加样式 webkit-playsinline、 playsinline <video controls="" height="auto" id="video1" playsinline="true" preload="auto" webkit-playsinline="true" width="100%"><source src="xxxxx.mp4" type="video/mp4"> 您的浏览器不支持 HTML5 video 标签。 </source></video> native code 设置 allowsInlineMediaPlayback WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; config.allowsInlineMediaPlayback = YES; WKWebView *webView = [[WKWebView alloc]initWithFrame:CGRectZero configuration:config]; 参考 https://www.zhihu.com/question/21094425

November 8, 2021 · 1 min · holdsky

WebRTC初学Demo

webrtc介绍 简介实现基本的数据通道文件传输修改传输速度限制选择并读取文件数据自动下载文件完整代码音视频通话https访问音视频流完整代码屏幕共享捕捉屏幕完整代码NAT穿透之STUN/TURN信令服务器信令服务实现完整代码 详细请访问 简介 WebRTC(网页实时通信技术),是一系列为了建立端到端文本或者随机数据的规范,标准,API和概念的统称。 任何实现了WebRTC标准的软件之间均可通信,如PC浏览器–手机浏览器、浏览器–App、App–App。通信双方是对等的,但通常还要引入服务端,以便于对等端能够找到对方。 移动端浏览器对WebRTC的支持 Android 4.4以上 iOS 11以上 WebRTC应用不需要非常高性能就能够平稳运行、拥有良好体验。对于WebRTC来说,获取特定硬件的权限也不是必需的。Web应用和原生应用对比: 原生应用会比Web应用更快,拥有更高级别的硬件权限;混合应用的运行速度更慢,不能使用移动设备的全部权限。 原生应用在不同的平台上的代码复用度有待提高;混合应用开发起来会更快,更省钱 结论:如果高性能和极致体验不是软件的必不可少的要求,那么,在绝大多数情况下,用户根本注意不到原生和混合WebRTC应用之间的差别。 实现基本的数据通道 对浏览器而言,WebRTC API处于底层且十分复杂。不过我们可以使用封装好的高级别的API—- simple-peer 是一个基础的,非常洁净的低层P2P连接封装器.下面的示例是 simple-peer 的一个标准例子。 <html> <body> <style> #outgoing { width: 100%; word-wrap: break-word; white-space: normal; } </style> <form> <textarea id="incoming"></textarea> <button type="submit">submit</button> </form> <pre id="outgoing"></pre> <script src="simplepeer.min.js"></script> <script> const p = new SimplePeer({ initiator: location.hash === '#1', trickle: false }) p.on('error', err => console.log('error', err)) p.on('signal', data => { console.log('SIGNAL', JSON.stringify(data)) document.querySelector('#outgoing').textContent = JSON.stringify(data) }) document.querySelector('form').addEventListener('submit', ev => { ev.preventDefault() p.signal(JSON.parse(document.querySelector('#incoming').value)) }) p.on('connect', () => { console.log('CONNECT') p.send('whatever' + Math.random()) }) p.on('data', data => { console.log('data: ' + data) }) </script> </body></html> 演示步骤: ...

October 19, 2021 · 9 min · holdsky

关闭Spotlight

原因无他,导致mac笔记本发热严重,要是一两次也就罢了,每天至少一次CPU 100%,打开活动监视器,都是这个Spotlight造成的。 步骤如下 关闭System Integrity Protection 进入恢复模式:重启电脑, 按住 Command 和 R。 输入命令 csrutil disable 因为Spotlist服务受System Integrity Protection保护,不关闭的话有可能无法关闭Spotlist服务,报错如下 Unload failed: 150: Operation not permitted while System Integrity Protection is engaged 关闭Spotlight服务 sudo mdutil -a -i off sudo mdutil -a -d sudo launchctl unload -w /System/Library/LaunchAgents/com.apple.Spotlight.plist 如果收到报错 Unload failed: 113: Could not find specified service 那就先加载Spotlight服务 sudo mdutil -a -i on sudo launchctl load -w /System/Library/LaunchAgents/com.apple.Spotlight.plist 然后再执行服务卸载 打开System Integrity Protection 进入恢复模式:重启电脑, 按住 Command 和 R。 输入命令 csrutil enable https://macpaw.com/how-to/disable-enable-system-integrity-protection

October 14, 2021 · 1 min · holdsky

iOS HealthKit 健康框架简介

详细介绍参考 https://developer.apple.com/documentation/healthkit Demo下载 简介 ​ HealthKit 为 iPhone 和 Apple Watch 上的健康和健身数据提供了一个中央存储库。在用户许可的情况下,应用程序与 HealthKit 通信以访问和共享此数据: 收集和存储健康和健身数据 分析和可视化数据 社交互动 下图来自于苹果开发者网站 ​ HealthKit 管理和合并来自多个来源的数据,用户可以在健康应用中查看和管理他们的所有数据,包括添加数据、删除数据和更改应用的权限;用户还可以编辑每种数据类型的分享权限。 隐私 ​ 由于健康数据可能包含敏感的个人信息,因此应用程序必须获得用户的许可才能从 HealthKit 存储区读取数据或向其写入数据,并且应用程序还必须始终采取措施保护这些数据。 ​ HealthKit 为用户提供了更细粒度的数据权限控制。用户必须明确授予每个应用对 HealthKit 存储区读取和写入数据的权限。用户可以针对每种类型的数据分别授予或拒绝权限。例如,用户可以让允许应用读取步数数据,但不允许读取血糖水平。 数据加密存储 ​ 用户的设备在本地存储所有 HealthKit 数据。为了安全起见,当用户锁定设备时,设备会加密 HealthKit 存储。因此,应用在后台运行时可能无法HealthKit存储中读取数据。但是手机被锁定时,应用程序仍然可以写入HealthKit存储。HealthKit 会临时缓存数据,并在用户解锁手机后立即将其保存到加密存储中。 指定应用如何使用健康数据 ​ 除非应用旨在提供健康或健身服务,否则应用不得访问 HealthKit API( 注意:影响应用上架)。此外,作为健康和健身服务的角色必须在营销文本和用户界面中明确。具体来说,以下指南适用于所有 HealthKit 应用程序: 您的应用不得将通过使用 HealthKit 框架获得的信息用于广告或类似服务。 仍然可以在使用 HealthKit 框架的应用程序中投放广告,但不能使用 HealthKit中的数据来投放广告 未经用户明确许可,不得将通过 HealthKit 获得的任何信息透露给第三方。 即使获得许可,也只能将信息分享给向用户提供健康或健身服务的第三方。 不能将通过 HealthKit 获得的信息出售给广告平台、数据经纪人或信息经销商。 如果用户同意,可以与第三方共享他们的 HealthKit 数据以进行 医学研究。 必须向用户明确披露您和您的应用将如何使用他们的 HealthKit 数据。 提供隐私政策 ​ 必须为使用 HealthKit 框架的任何应用程序提供隐私政策。可以在以下站点找到有关创建隐私政策的指南: ...

October 13, 2021 · 2 min · holdsky

Safari Websocket: Invalid HTTP version string: HTTP/1.0

原因是Safari的Websocket连接是基于HTTP/1.1的,需要把Websocket Sever的应答也基于HTTP/1.1 一个不合适应答报文示例: 1、Safari请求基于HTTP/1.1建立WebSocket连接 2、Server应答为基于HTTP/1.0,那么Safari将关闭本次WebSocket连接请求 GET / HTTP/1.1 #Safari请求基于HTTP/1.1建立WebSocket连接 Upgrade: websocket Connection: Upgrade Host: localhost:8000 Origin: http://localhost:8000 Pragma: no-cache Cache-Control: no-cache Sec-WebSocket-Key: psuWu4PKpvC5PlimntI1uw== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: x-webkit-deflate-frame User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Safari/605.1.15 HTTP/1.0 101 Switching Protocols #Server应答为基于HTTP/1.0,那么Safari将关闭本次WebSocket连接请求 Server: SimpleHTTP/0.6 Python/2.7.15 Date: Wed, 08 Sep 2021 07:26:20 GMT Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: Mwq3JIMwIENZgkjooYLJobX4L90=

September 8, 2021 · 1 min · holdsky

macOS 清理磁盘空间

1、缓存和日志 ~/Library/Caches ~/Library/Logs 这两个可以安全地删除 2、其他 - Application Support 里面存放三方App的一些缓存和记录,可以挑选删除。 如 VSCode 相关 ~/Library/Application Support/Code/User/workspaceStorage ~/Library/Application Support/Code/Cache* 3、Xcode 执行命令 xcrun simctl delete unavailable 删除 ~/Library/Developer/CoreSimulator/Caches https://zhuanlan.zhihu.com/p/349862578

September 7, 2021 · 1 min · holdsky