作者: xiao, yanzi

IOS的app电量测试

需要工具Sysdiagnose,只需要安装一个证书即可。如下:
下载、安装、使用:

证书下载:https://developer.apple.com/bug-reporting/profiles-and-logs/,下载证书。
把证书发送到需要测试电量的手机上,安装证书
安装完成后同时按住+ -音量键后按一下电源键,手机震动一下说明启动成功.可以对APP进行操作测试。
等待30分钟后通过itunes把数据同步到电脑上(PLSQL后缀的一个数据库文件),通过sqlite打开文件即可(DB Browser for Sqlite)。
原理:非充电模式,启动后每隔1个小时数据写入表一次,等待写入表后方可查看电量情况。有些表充电时不记录数据。

数据位置:

C:\Users\allin\AppData\Roaming\Apple Computer\Logs\CrashReporter\MobileDevice\iPhone123

 

Plsql数据库文件中重要的表:

%title插图%num

PLBatteryAgent_EventBackward_Battery

PLBatteryAgent_EventBackward_BatteryUI

PLApplicationAgent_EventNone_AllApps

PLAppTimeService_Aggregate_AppRunTime

PLAccountingOperator_Aggregate_RootNodeEnergy

PLAccountingOperaor_EventNone_Nodes

PLAccountingOperator_Aggregate_RootNodeEnergy表中字段RootNodeID含义:

node 6 isp图像处理器
node 52 apsocbase除cpu外芯片层耗电
node 10 display 显示
node 11 wifidata
node 8 GPU
node 4 venc视频编码器
node 2 cpu
node 7 restofsoc

注:数据表中记录的数据跟随IOS系统而定。因为没有相关文档,通过推敲判断:Ios9 – mWh;Ios10\11 – 1/1000 mWh。仅供参考~

tencent GT ios测试电量

Temperature: 电池温度。

currentcapacity:电池当前的剩余容量。

maxcapacity:电池*大容量。 这个是显示你的电池完全充电之后的容量,从此可以看出电池损耗。
designcapacity:电池设计容量。 这个是iphone电池出厂设计容量,也就是标准容量。
battery charge:电池当前剩余容量百分比(充电百分比)。
cyclecount:电池完全充电循环次数。
voltage:当前电池电压。
bootvoltage:电池启动电压。
drain:耗电量。这个是电池当前的耗电情况,重要指标。

对比GT的电量信息图:红框处即为消耗的电量。红色框的值/MaxCapacity 即为消耗的电量百分比。

%title插图%num

iOS 如何做扫码安装

xcode 打企业包,或者给测试打包的时候,不需要每次都对测试机插在mac上给他们安装,*简单的方法是做个二维码,让他们去扫描安装.

步骤一
首先导出ipa包。
步骤二
把ipa包放到一个服务器上面,以便其他人下载.

步骤三
做一个plist文件内容:并且把这个plist文件放到https服务器上,我放在http://git.oschina.net 这个上面

1 <?xml version=”1.0″ encoding=”UTF-8″?>
2 <!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
3 <plist version=”1.0″>
4 <dict>
5     <key>items</key>
6     <array>
7         <dict>
8             <key>assets</key>
9             <array>
10                 <dict>
11                     <key>kind</key>
12                     <string>software-package</string>
13                     <key>url</key>
14                     //这个地方写ipa地址
15                     <string>http://123.123.123.123:1111/maarrytask.ipa</string>
16                 </dict>
17                 <dict>
18                     <key>kind</key>
19                     <string>full-size-image</string>
20                     <key>needs-shine</key>
21                     <false/>
22                     <key>url</key>
23                     <string></string>
24                 </dict>
25                 <dict>
26                     <key>kind</key>
27                     <string>display-image</string>
28                     <key>needs-shine</key>
29                     <false/>
30                     <key>url</key>
31                     <string>安装过程显示图片</string>
32                 </dict>
33             </array>
34             <key>metadata</key>
35             <dict>
36                 <key>bundle-identifier</key>
37                 <string>com.xianhenet.hunpopo</string>
38                 <key>bundle-version</key>
39                 <string>1.0</string>
40                 <key>kind</key>
41                 <string>software</string>
42                 <key>title</key>
43                 <string>packName</string>
44             </dict>
45         </dict>
46     </array>
47 </dict>
48 </plist>

做一个下载页面

1 <!doctype html>
2 <html>
3 <head>
4 <meta charset=”utf-8″>
5 <meta name=”viewport” content=”width=device-width; initial-scale=1.0″>
6 <meta name=”keywords” content=”test” />
7 <meta name=”description” content=”” />
8 <title>婚派测试包</title>
9 <link rel=”stylesheet” type=”text/css” href=”style/css/mobile.css” />
10 </head>
11 <body>
12 <div class=”doc”>
13 <br/>
14 <br/>
15 <br/>
16 <br/>
17 <br/>
18 <br/>
19 <p align=”center”><a href=”itms-services://?action=download-manifest&url=/*plist文件位置*/https://git.oschina.net/raw/master/Release.plist”><img alt=”” src=”images/icon.png” style=”height:160px; width:160px”/></a></p>
20 <br/>
21 <br/>
22 </div>
23 </body>
24 </html>

*后把这个下载也放在服务器上,提供访问地址即可,可以把访问地址生成二维码

iOS测试包安装途径实践

概述:
iOS开发的应用由于受到开发证书的限制,想要在一台苹果设备上,安装app以测试,还需要一些额外的操作;

我们知道,苹果应用的下载渠道与苹果应用的开发者账号类型有关;

企业级的苹果开发者证书分为2种:

1)99美元的企业账号;

2)299美元的企业账号;

其中,99美元的账号开发的应用上传至App Store供用户下载;

299美元的账号开发的应用可供企业内部分发;

有关开发者证书申请流程可参阅《苹果开发者证书申请流程》一文;

分发和测试:
对于企业级内部分发的包:

如果是发布包,可通过安装包或生成的安装包二维码进行安装,安装前需要信任下企业证书,比较简单,这里就不赘述了;

如果是内部测试包,即Adhoc的包,参考接下来公司级应用Adhoc包的安装即可

着重说一下公司级应用需要通过App Store分发的包,如需安装测试,都有哪些途径;

1.通过TestFlight;

2.通过开发者注册开发设备安装adhoc的包;

TestFlight:
一款苹果公司提供的开发者工具,类似一个盗版的AppStore,可用于管理测试的应用的beta版,同时可邀请和管理指定用户进行测试,“以便在应用正式发布之前获得更多有价值的用户体验反馈”;

(图示:ios构建的版本)

%title插图%num

(图示:内部和外部测试说明)

%title插图%num

(图示:所有测试人员)

%title插图%num

App Store Connect用户:

%title插图%num

(图示:App Store Connect用户添加)

添加外部测试员:

%title插图%num
(图示:添加群组)

基于已经添加的群组进行测试版本构建:
(图示:构建版本)

%title插图%num

Beta版App审核:
(图示:测试信息填写)

%title插图%num

之后会对测试版本进行审核,审核通过的beta版,测试用户会收到相应的邮件通知;

值得注意的是,这里选择的构建版本,需要是提交AppStore审核的构建版本,不管审核是否通过,都可以在这里提交审核,只要beta版审核通过就可以通过TestFlight进行分发测试了;

我们可以通过App Store下载TestFlight:

%title插图%num

(图示:App Store显示)(图示:管理界面)(图示:invite you to test)(图示:Stop testing)

测试员收到的邮件示例:(外部测试员 还需要在TestFlight中填写Redeem Code)

现在测试员就可以通过TestFlight 初始化App的Beta版了;

用户自己也可以“Stop testing”来推出测试,当然开发者可以控制所有测试员的权限;

 

崩溃报告:
TestFlight测试的报告会与开发者共享,可以通过Xcode进行崩溃报告查看:

(图示:查看崩溃报告)

%title插图%num

*后提醒下:通过苹果提供的TestFlight,在iTunes Connect上添加*多25名测试人员;以及beta版审核通过之后*多10000名外部测试人员;应用测试的评估时长是90天;TestFlight归苹果公司所有。

安装Adhoc包:
对于99美元的开发者账号,如果App尚未通过苹果认证(未上传 或者 beta版无法通过测试),安装测试的话就只能通过这种方式:将测试人员的设备标识注册到开发者账号,以安装应用测试包;

设备标识通常也叫做UDID;它可以唯一标示iOS设备;

获取UDID:
获取方式1:链接电脑,查看设备信息,保存即可;

(图示:通过电脑获取设备标识)

%title插图%num

获取方式2:通过蒲公英提供的服务,访问网址:一步快速获取iOS设备UDID;

(图示:蒲公英主页)

%title插图%num

使用微信扫一扫:按照提示在浏览器中打开网页:

%title插图%num

(图示)

点击获取UDID:根据指引需要安装一个描述文件,点击安装,如提示输入密码,输入锁屏密码即可:

%title插图%num

(图示)

操作完成后,Safari会打开如下页面:你可以直接复制或者使用邮箱接收保存:

%title插图%num

(图示:邮箱接收)

在得到UDID之后,将该信息及相关信息以如下格式提供公司开发者(开发者进行设备添加和证书更新,重新打包):

参考格式要求:用户昵称_用户机型_UDID;

监鉴于测试设备数量限制上限100(当前结算年无效的设备也占数),可接受测试人员的数量不可太多(20~50);

 

已提供UDID的用户,并由开发人员提供测试包安装或相应的二维码即可下载或扫描下载安装:

蒲公英平台也提供相应的二维码下载安装途径:

(图示:通过二维码安装测试包)

%title插图%num

over;

 

补充:
如果只能使用adhoc包的方式,则每年在证书续费的时候,记得将无效的设备删除掉,如果实在是不够用可以通过向苹果反馈;

Thanks for everybody!

iOS测试和Android测试的区别

iOS测试和Android测试的区别
IOS 和安卓系统比较
1、系统与框架结构
2、渲染机制
2.1 iOS*先响应屏幕
2.2 iOS系统优先处理Touch层级
2.3 iOS图形特效基于GPU加速渲染
iOS测试和Android测试
Android以及iOS是当下手机的两大主流操作系统,APP测试中,iOS测试和Android测试需要分开测试。

IOS 和安卓系统比较
从以下几个方面,先简单了解一下这两大操作系统的不同之处

1、系统与框架结构
Android系统的底层建立在Linux系统之上;而ios基于UNIX系统。
这一点就造成了Android与iOS的生态不同了,Android完全开源,任何软件开发商或者个人都能开发安卓的软件;ios完全封源开发。

Android的编程语言是Java和KotLin;而ios的则为ObjectC和Swift。
Android的Java,面向对象,性能比C语言和OC低;ios的OC,基于对象,完全兼容C语言的语法,可以直接操作内存。
Android生成class文件,需要虚拟机来进行解释;ios直接执行程序的二进制代码。
这也在根本上造成了iOS与Android性能不同:Android和Window一样,目的是打造一款通用性非常好的系统,在任何机器上面都可以运行;ios目的是让软件和硬件完美的结合到一块,该操作系统只能在*少数机器上面才能运行。

iOS与Android的运行机制:ios采用的是沙盒运行机制;安卓采用的是虚拟机运行机制。
iOS采用伪后台,当用户HOME键退出应用时,IOS其实关闭了程序,只保留应用的图像入口,只会默认将*后的运行数据记录在RAM中。之所以IOS也能收到推送,是因为应用程序开启推送后,系统会增加一些进程,这些进程会从苹果服务器接收信息,然后再通过服务器发给用户,苹果服务器在这里是起到了中转的作用。
安卓手机的后台是真后台,将应用保留在RAM中,之所以能够收到推送,也因为它常驻内存。
所以Android在软件关闭的情况下,无法接收推送信息;ios在软件关闭的情况下,依然可以接收推送信息
iOS系统在系统内存不足时会自动释放内存。

2、渲染机制
2.1 iOS*先响应屏幕
IOS的UI渲染采用实时优先级,Android的UI渲染遵循传统电脑模式的主线程普通优先级

IOS的响应顺序依次为Touch–Media–Service–Core架构
Android系统的优先级响应层级是Application–Framework–Library–Kernal架构

当我们使用iOS或者是Android手机时,*步就是滑屏解锁找到相应程序点击进入。而这个时候往往是所有操控开始的*步骤,iOS系统产品就表现出来了流畅的一面,但Android产品却给人一种卡顿的现象,更别说后续深入玩游戏或者进行其它操控了。

这也就是为什么我们常说iOS比Android流畅了,因为iOS*先响应屏幕,iOS对屏幕反应的优先级是*高的,它的响应顺序依次为Touch–Media–Service–Core架构,换句话说当用户只要触摸接触了屏幕之后,系统就会*优先去处理屏幕显示也就是Touch这个层级,然后才是媒体(Media),服务(Service)以及Core架构。

而Android系统的优先级响应层级则是Application–Framework–Library–Kernal架构,和显示相关的图形图像处理这一部分属于Library,你可以看到到第三位才是它,当你触摸屏幕之后Android系统首先会激活应用,框架然后才是屏幕*后是核心架构。

2.2 iOS系统优先处理Touch层级
优先级的不同导致了iOS产品以及Android手机在操控过程中的表现差异,当你滑动屏幕进行操控的时候,iOS系统会优先处理Touch层级,而Android系统则是第三个才响应Library层级,这是造成它们流畅度不同的因素之一。

不过优先级对系统流畅性有有影响不假,但并不是**对的,造成两系统之间流畅性不一的现象还有其它因素。 目前智能手机硬件装备竞赛当中,其实处理器等配置已经达到了一个瓶颈期,各大旗舰产品在硬件比拼当中基本上没有太大的区别,而这时候GPU就成为了一个凸显差异的重要因素。一些大型软件像是3d游戏对GPU性能要求都会比较高,苹果iPhone产品采用的Power VR SGX系列GPU在当下来说非常的主流,跑分测试数据证明了它并不会比一些旗舰级别的Android产品差劲。

2.3 iOS图形特效基于GPU加速渲染
iOS系统对图形的各种特效处理基本上正好都是基于GPU硬件进行加速的,它可以不用完全借助CPU或者程序本身,而是通过GPU进行渲染以达到更流畅的操控表现。

但是Android系统产品则并非如此,因为Android需要适应不同的手机硬件,需要满足各种差异配置,所以很多图形特效大多都要靠程序本身进行加速和渲染,并严重依赖CPU运算的操作自然会加大处理器的负荷,从而出现卡顿的问题。

iOS系统对图形的各种特效处理基本上正好都是基于GPU硬件进行加速的,它可以不用完全借助CPU或者程序本身,而是通过GPU进行渲染以达到更流畅的操控表现。
Android系统产品则并非如此,因为Android需要适应不同的手机硬件,需要满足各种差异配置,所以很多图形特效大多都要靠程序本身进行加速和渲染。
(虽然Android 4.0以及4.1等更高版本中进行了改进将硬件加速设为默认开启,但依旧无法做到所有特效全部都靠GPU进行加速。在很多Android手机里面都自带有“是否开启GPU渲染”这个功能选项,不过开启之后的改善也是微乎其微。)

屏幕*先响应的优先级关系,再加上iSO本身GPU加速程序的特性,使得在操控过程中感觉iOS手机拥有着良好的流畅性。因为它本身的整个流程都是在为*大化的流畅做服务,不管是*印象的滑动接触屏幕,还是你进一步使用程序之后的更深层操作都是如此。而GPU加速这点特性,是它优于Android系统流畅性的又一个因素。

iOS测试和Android测试
APP测试中,iOS测试和Android测试主要会针对以下几个点进行测试:

分辨率的测试:Android端有20多种,iOS相对少一点。
操作系统版本:Android的操作系统版本比较多,现在常见的是Android9和Android10,还有不同手机厂商的版本,比如小米的MIUI,魅族的Flyme;
iOS的比较少,而且它只支持单向升级,不能支持降级。
操作习惯的不同:像Android,习惯的去点击back键,虽然现在很多都是全面屏,都是通过手势滑动返回,但还是属于back键的功能,所以Android需要测试back键是否被重写了,点击了back键系统的反馈是不是正常的。
推送消息的测试:Android点击home键后,程序运行到后台,那么这个时候,推送消息是否可以正常被推送,以及点击应用程序,唤醒到前台运行的时候,然后点击消息,是否可以正常的跳转;
iOS点击Home键或是锁屏,或者是关闭程序的时候,消息推送是否是正常的。
安装和卸载测试:Android的安装的平台和渠道相对会比较多,而iOS的话一般只支持官方的渠道比如说 APP store 、 iTunes 工具以及 testflight 的下载

iOS iOS NSDate 时间计算

打印效果:

%title插图%num
#import “ViewController.h”

@interface ViewController ()

@end

@implementation ViewController

– (void)viewDidLoad

{

[super viewDidLoad];

//当前时间

[self currentTime];

//当年共多少天

[self yearTotalDays];

//相差几天

[self quiteFewDays];

}

/**

当前时间

*/

– (void)currentTime

{

//得到当前的时间

NSDate * date = [NSDate date];

//时间管理

NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];

//时间格式

[dateFormatter setDateFormat:@”yyyy-MM-dd HH:mm:ss”];

//设置时间间隔(秒)(这个我是计算出来的,不知道有没有简便的方法 )

NSTimeInterval time = 365 * 24 * 60 * 60;//一年的秒数

//得到一年之前的当前时间(-:表示向前的时间间隔(即去年),如果没有,则表示向后的时间间隔(即明年))

 

NSDate * lastYear = [date dateByAddingTimeInterval:-time];

//转化为字符串

NSString * startDate = [dateFormatter stringFromDate:lastYear];

NSString *dayYY = [NSString stringWithFormat:@”%@”,startDate];

NSLog(@”dayYY—> %@”,dayYY);

}

/**

 

当年共多少天

*/

– (void)yearTotalDays

{

//2.当年共多少天

NSCalendar *calender = [NSCalendar currentCalendar];

//计算时间

NSDateComponents *comps =[calender components:(NSYearCalendarUnit | NSMonthCalendarUnit |NSDayCalendarUnit | NSWeekCalendarUnit)fromDate:[NSDate date]];

 

int count = 0;

for (int i=1; i<=12; i++) {

 

[comps setMonth:i];

 

NSRange range = [calender rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate: [calender dateFromComponents:comps]];

 

count += range.length;

}

NSLog(@”count—>%d”, count);

}

/**

相差几天

*/

– (void)quiteFewDays

{

//3.当时时间距离未来(过去)的天数

NSString * time = @”2017-06-07″;  //可以写过去时间差

NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];

[dateFormatter setDateFormat:@”yyyy-MM-dd”];

 

//根据需求 选择date的方法

NSDate * date = [dateFormatter dateFromString:time];//x现在到未来的时间差

//NSDate * date = [time dateFromString:dateFormatter];//现在和以前的时间差天数

NSDate * currentDate = [NSDate date];

 

NSUInteger sec = [date timeIntervalSinceDate:currentDate];

NSLog(@”sec—->%zd”,sec/(3600*24));

}

 

iOS Mac下SVN环境的配置方法

友情提示:特别轻松的配置 svn

一、创建代码仓库:

1.打开终端,输入创建一个仓库的指令,如:svnadmin create /Users/svn/mycode

%title插图%num

二、配置svn的用户权限:

2.权限配置文件在 /svn/mycode/conf目录下;

打开svnserve.conf,主要配置项前面的“#”和空格都去掉;

如图:

%title插图%num

如图:正确的效果

%title插图%num

三、第二个权限配置文件passwd,在[users]下面添加帐号和密码

如图:正确的效果

%title插图%num

四、第三个权限配置文件authz,可以配置用户组和权限:

在[groups]下面添加组名和用户名,多个用户之间用逗号隔开,

如图:正确的效果

%title插图%num

注意点:使用[/]代表svn服务器中的所有资源库,如图,group这个组中的所有用户对所有资源库都有读写(rw)权限,组名前面要用@

五、启动svn服务器:

终端输入启动服务器的指令:svnserve -d -r /Users/svn

没有任何提示才是说明启动成功,

六、启动失败:svnserve: Can’t bind server socket: Address already in use 错误

解决svnserve: Can’t bind server socket: Address already in use

怎么解决呢?

killall  svnserve

svnserve -d -r /opt/svn/repos   启动成功。

图:

%title插图%num

七、关闭svn服务器:

关闭svn服务器要在实用工具里面打开“活动监视器”操作

如下图:

%title插图%num

八、导入代码

svn import  代码的路径     就可以下载服务器的代码

谢谢!!!

iOS – 底层知识学习之路-Mach-o 、lldb、 dyld

MACHO与重定位符号表认识。
命令回顾

1. 将源代码编译成可执行文件命令

Clang 文件名 -o 输出的可执行文件名称

如:
Clang test.m – o test
2. 查看可执行文件的代码段命令

objdump –macho -d 可执行文件名称

如:
objdump –macho -d test
3. 将文件编译生.o文件命令

clang -c 文件名 -o .o文件名
如:
clang -c test.m -o test.o
4. 分析.o文件的代码段命令

源码:

void test() {

}
void test_1() {

}
int global = 10;
int main(){
global = 21;
global = 20;
test();
test_1();
return 0;
}
执行:

objdump –macho -d test.o
结果:

(__TEXT,__text) section
_test:
0: 55 pushq %rbp
1: 48 89 e5 movq %rsp, %rbp
4: 5d popq %rbp
5: c3 retq
6: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:_test(%rax,%rax)
_test_1:
10: 55 pushq %rbp
11: 48 89 e5 movq %rsp, %rbp
14: 5d popq %rbp
15: c3 retq
16: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:_test(%rax,%rax)
_main:
20: 55 pushq %rbp
21: 48 89 e5 movq %rsp, %rbp
24: 48 83 ec 10 subq $16, %rsp
28: c7 45 fc 00 00 00 00 movl $_test, -4(%rbp)
2f: c7 05 fc ff ff ff 15 00 00 00 movl $21, _global-4(%rip)
39: c7 05 fc ff ff ff 14 00 00 00 movl $20, _global-4(%rip)
43: e8 00 00 00 00 callq _test
48: e8 00 00 00 00 callq _test_1
4d: 31 c0 xorl %eax, %eax
4f: 48 83 c4 10 addq $16, %rsp
53: 5d popq %rbp
54: c3 retq
得出结论:

1. 看汇编可以得出编译顺序与代码的书写顺序是一致的。

2. 看 43 与 48 行地址为 00 ,这是虚拟地址, 起占位作用, 通过重定位符号表来确定*终的内存地址。

3. 查重定位符号表

objdump –macho –reloc .o文件名
如:
objdump –macho –reloc test.o
结果:如下面结果 (43 , 48 的下一位)49 与 44 分别就是上面占位地址的*终内存地址 。

address pcrel length extern type scattered symbolnum/value
00000049 True long True BRANCH False _test_1
00000044 True long True BRANCH False _test
0000003b True long True SIGNED4 False _global
00000031 True long True SIGNED4 False _global
Relocation information (__LD,__compact_unwind) 3 entries
address pcrel length extern type scattered symbolnum/value
00000040 False quad False UNSIGND False 1 (__TEXT,__text)
00000020 False quad False UNSIGND False 1 (__TEXT,__text)
00000000 False quad False UNSIGND False 1 (__TEXT,__text)

%title插图%num

分析可执行文件的代码段命令

objdump –macho -d test
输出:

(__TEXT,__text) section
_test:
100003f60: 55 pushq %rbp
100003f61: 48 89 e5 movq %rsp, %rbp
100003f64: 5d popq %rbp
100003f65: c3 retq
100003f66: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax)
_test_1:
100003f70: 55 pushq %rbp
100003f71: 48 89 e5 movq %rsp, %rbp
100003f74: 5d popq %rbp
100003f75: c3 retq
100003f76: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax)
_main:
100003f80: 55 pushq %rbp
100003f81: 48 89 e5 movq %rsp, %rbp
100003f84: 48 83 ec 10 subq $16, %rsp
100003f88: c7 45 fc 00 00 00 00 movl $0, -4(%rbp)
100003f8f: c7 05 67 40 00 00 15 00 00 00 movl $21, 16487(%rip)
100003f99: c7 05 5d 40 00 00 14 00 00 00 movl $20, 16477(%rip)
100003fa3: e8 b8 ff ff ff callq _test
100003fa8: e8 c3 ff ff ff callq _test_1
100003fad: 31 c0 xorl %eax, %eax
100003faf: 48 83 c4 10 addq $16, %rsp
100003fb3: 5d popq %rbp
100003fb4: c3 retq
mac OS 是小端。  地址从右到左查看 。  右是高位

 

DWARF与DSYM
dsym 文件就是保存按照DWARF格式保存的调试信息的文件 。

dwarf 是一种被众多编译器和调试器使用的用于支持源码代码级别调试的调试文件格式。

dsym文件的生成过程:

1. 读取debug map

2. 从.o文件中加载 dwarf

3. 重新定位所有的地址

4. *后将全部的 dwarf 打包成 dsym bundle

生成调试信息命令

clang -g -c test.m -o test.o
理解第2步的过程: 终端命令 查看 test.o

otool -l test.o
结果:  下面就有dwarf 字段 。

Load command 0
cmd LC_SEGMENT_64
cmdsize 1112
segname
vmaddr 0x0000000000000000
vmsize 0x00000000000004c9
fileoff 1272
filesize 1225
maxprot 0x00000007
initprot 0x00000007
nsects 13
flags 0x0
Section
sectname __text
segname __TEXT
addr 0x0000000000000000
size 0x0000000000000055
offset 1272
align 2^4 (16)
reloff 2500
nreloc 4
flags 0x80000400
reserved1 0
reserved2 0
Section
sectname __data
segname __DATA
addr 0x0000000000000058
size 0x0000000000000004
offset 1360
align 2^2 (4)
reloff 0
nreloc 0
flags 0x00000000
reserved1 0
reserved2 0
Section
sectname __objc_imageinfo
segname __DATA
addr 0x000000000000005c
size 0x0000000000000008
offset 1364
align 2^0 (1)
reloff 0
nreloc 0
flags 0x10000000
reserved1 0
reserved2 0
Section
sectname __debug_str
segname __DWARF
addr 0x0000000000000064
size 0x0000000000000111
offset 1372
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __debug_abbrev
segname __DWARF
addr 0x0000000000000175
size 0x0000000000000061
offset 1645
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __debug_info
segname __DWARF
addr 0x00000000000001d6
size 0x0000000000000093
offset 1742
align 2^0 (1)
reloff 2532
nreloc 5
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_names
segname __DWARF
addr 0x0000000000000269
size 0x0000000000000090
offset 1889
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_objc
segname __DWARF
addr 0x00000000000002f9
size 0x0000000000000024
offset 2033
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_namespac
segname __DWARF
addr 0x000000000000031d
size 0x0000000000000024
offset 2069
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
Section
sectname __apple_types
segname __DWARF
addr 0x0000000000000341
size 0x0000000000000047
offset 2105
align 2^0 (1)
reloff 0
nreloc 0
flags 0x02000000
reserved1 0
reserved2 0
如果是查看可执行文件,是找不到drarf字段的, 他会单独放在一个地方 ,通过命令查看:

nm -pa 可执行命令文件名
如:
nm -pa test
生成dsym文件命令:

clang -g1 test.m -o test
查看 dsym 命令:

dwarfdump test.dSYM
dsym 内文件中保存的是没有偏移的虚拟地址,所以能够进行bug的现场恢复。

 

计算虚拟地址
ViewController.m文件如下:

1. 获取 ASLR

2. 计算虚拟地址 。

#import “ViewController.h”
#import <mach-o/dyld.h>
#import <mach-o/getsect.h>
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

// 获取ASLR
uintptr_t get_slide_address(void) {
uintptr_t vmaddr_slide = 0;
// 使用的所有的二进制文件 = ipa + 动态库
// ASLR Macho 二进制文件 image 偏移
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
// 遍历的是那个image名称
const char *image_name = (char *)_dyld_get_image_name(i);
const struct mach_header *header = _dyld_get_image_header(i);
if (header->filetype == MH_EXECUTE) {
vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
}
NSString *str = [NSString stringWithUTF8String:image_name];

if ([str containsString:@”TestInject”]) {

NSLog(@”Image name %s at address 0x%llx and ASLR slide 0x%lx.\n”, image_name, (mach_vm_address_t)header, vmaddr_slide);
break;
}
}

// ASLR返回出去
return (uintptr_t)vmaddr_slide;
}

– (void)viewDidLoad {
[super viewDidLoad];
[self getMethodVMA];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self test_dwarf];
});
NSLog(@”123″);
}

– (void)test_dwarf {
NSArray *array = @[];
array[1];
}

– (void)getMethodVMA {
// 运行中的地址(偏移)
IMP imp = (IMP)class_getMethodImplementation(self.class, @selector(test_dwarf));
unsigned long imppos = (unsigned long)imp;
unsigned long slide = get_slide_address();
// 运行中的地址(偏移) – ASLR = 真正的虚拟内存地址
unsigned long addr = imppos – slide;
}
@end
注意: 需要在building setting里面打开生成dysm文件的配置 。  debug默认不生成这个文件 。

xcode 终端格式输入十六进制   lldb调试输入

e -f x — 计算出的虚拟内存地址

结论:

使用的所有的二进制文件 = ipa + 动态库

ASLR Macho 二进制文件 image 偏移

 

 

dyld学习 – 后期通过源码深入学习
dyld 调试:

方式一: 如果想调试dyld的源代码,需要准备带调试信息dyld/libdyld.dylib/libclosured.dylib,与系统做替换,风险比较大。

方式二 : 通过在dyld文件上设置断点。  设置断点的方法有两种。

lldb保留了一个库列表(白名单), 避免在按名称设置短点时出现问题, 而 dyld 正好在这个名单上,所以需要强制设置短点, 方式如下两种:

-s 含义: 指定在哪个文件里面设置断点。

1. br set -n dyldbootstrap::start -s dyld

2. set set target.breakpoints-use-platform-avoid-list 0   (通过lldb)

%title插图%num

终端输入: 打印dyld所有的调用过程 、 其他配置自行查询实践。

DYLD_PRINT_APIS=1 ./ 可执行文件
如:
DYLD_PRINT_APIS=1 ./test
dyld 到底做了什么?

dyld: 动态链接程序

libdyld.dylib :给我们的程序提供在runtime期间能使用动态链接功能。

具体执行步骤如下:

%title插图%num

插入动态库与插入函数: hook

插入函数 :

__attribute__((used))  : 去除未使用的代码的警告,加在*前面 。

__attribute__((used)) static struct {
const void* replacement;
const void* replacee;

%title插图%num

iOS开发之判断用户是否打开APP通知开关,看我就够了

1.*近在做app内部的推送开关功能。 这样顾客可以自己定义推送给他的内容,屏蔽不想要的一些烦人推送。

在开发过程中, 如果顾客打开推送开关的时候,也已经向服务器发送指令,进行推送开关同步,给它说这个用户已经打开了A推送,现在服务器推送A推送给客户端, 这时候照说,客服端是可以收到通知的,但是客服端却没有收到。 这是为什么呢? 很简单的一个问题,原来是顾客没有在系统通知处打开app的通知开关,所以收不到推送是正常现象。

那现在就产生了一个需求:

用户在进行设置推送开关的时候,当用户打开推送开关为开的时候,需要去判断 系统通知处的 推送开关用户有没有进行设置?

网上这样的代码都是一大把,关于怎么去检测系统通知有没有打开,但是,发现运用到程序中,没什么鸟用, 会一直提示说,用户没有打开推送。

下面就到了贴代码的环节了,首先先看下这段代码:

if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) { //判断用户是否打开通知开关
}
typedef NS_OPTIONS(NSUInteger, UIRemoteNotificationType) {//这个是用户当前通知的几种状态,*种就是用户没有开大通知开关
UIRemoteNotificationTypeNone = 0,
UIRemoteNotificationTypeBadge = 1 << 0,
UIRemoteNotificationTypeSound = 1 << 1,
UIRemoteNotificationTypeAlert = 1 << 2,
UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3,
} NS_ENUM_DEPRECATED_IOS(3_0, 8_0, “Use UIUserNotificationType for user notifications and registerForRemoteNotifications for receiving remote notifications instead.”) __TVOS_PROHIBITED;
我在程序中就是用到了这个方法,去进行检测,用户有没有打开推送开关, 却忽视了

NS_ENUM_DEPRECATED_IOS(3_0, 8_0, “Use UIUserNotificationType for user notifications and registerForRemoteNotifications for receiving remote notifications instead.”) __TVOS_PROHIBITED

通过上面的图,可以看到此方法在iOS8.0就废弃了,虽然说并没有被移除,但是也要用他*新建议的方法去进行版本的兼容,现在加上在iOS9.0上用这个方法进行判断没有任何作用,现在就用新版的判断方法。下面是新版的判断方法。

if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIRemoteNotificationTypeNone) {
}
注意:
currentUserNotificationSettings 是一个对象,属于UIUserNotificationSettings类
所以.types切莫忘记。

总结:

*完善的做法就是,进行两者兼容 iOS7下的也要兼容, iOS7以上的我更要兼容啦,*完善的做法

 

#define IOS8 ([[[UIDevice currentDevice] systemVersion] doubleValue] >=8.0 ? YES : NO)

if (IOS8) { //iOS8以上包含iOS8
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIRemoteNotificationTypeNone) {
}
}else{ // ios7 一下
if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) {
}
}

 

iOS – 静态库、动态库从浅到深学习之路 (三)

XCFramework  (framework的增强版)
说明:

1. 苹果官方推荐,支持的,可以更加方便多个平台和架构的分发二进制库的格式。

2. 需要xcode11以上支持

3. 在2019年提出的framework的另一种先进格式。

多架构合并
架构打包命令:

// 打包成模拟器架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS Simulator’ \
-archivePath ‘../archives/SYTimer.framework-iphonesimulator.xcarchive’ \
SKIP_INSTALL=NO

// 打包成iOS真机架构
xcodebuild archive -project ‘SYTimer.xcodeproj’ \
-scheme ‘SYTimer’ \
-configuration Release \
-destination ‘generic/platform=iOS’ \
-archivePath ‘../archives/SYTimer.framework-iphoneos.xcarchive’ \
SKIP_INSTALL=NO
胖二进制: 多个架构将库文件压缩放在一起,并不是合并。

通过lipo 命令进行将动态库进行合并。

格式 : lipo -output 合并后输出的名称 -create  架构路径1 架构路径2

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer
遇到问题;

相同的架构合并,会出现冲突

解决方法:

去除相同的架构,只留一个架构,相关命令如下:

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer

//提取 x86_64架构
lipo -output SYTimer-x86_64 -extract x86_64 ../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer

lipo -output SYTimer -create ../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework/SYTimer SYTimer-x86_64
苹果为了解决这个问题,所以引进了XCFramework ,优点相比于 lipo

1.  不用处理头文件

2. 会自动处理重复的架构

3. 会自动产生调试符号

4. 会根据运行的架构自动选择对应的架构 。

 

制作一个XCFramework:
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-framework ‘../archives/SYTimer.framework-iphonesimulator.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-output ‘SYTimer.xcframework’ 需要改xcframework放的路径
xcodebuild -create-xcframework \
-framework ‘../archives/SYTimer.framework-iphoneos.xcarchive/Products/Library/Frameworks/SYTimer.framework’ \
-debug-symbols ‘架构1 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构2 – bcsymbolmap – 的*对路径’ \
-debug-symbols ‘架构1 – dsym – 的相对路径’ \
-framework ‘架构2 -framework – 的相对路径’ \
-debug-symbols ‘架构2 – dysm – 的相对路径’ \
-output ‘SYTimer.xcframework’
weak import
1. 弱引用的作用 ,就是当动态库被引入但没有使用的话,会自动置为nil ,不会让程序崩溃,编译通过,一个符号找不到,不会强制找到为止。 在xcconfig文件中格式为:

OTHER_LDFLAGS = $(inherited) -Xlinker -weak_framework -Xlinker “第三方库或动态库名称”
相同静态库的重复引入,符号冲突的解决方法:

通过链接器的 force_load 引入其中一个静态库, 另外一个静态库的符号通过 load_hidden 去隐藏。

命令如下:

OTHER_LDFLAGS = $(inherited) -l”*个静态库名” -l”第二个静态库名” -Xlinker -force_load -Xlinker “*个静态库路径.a” -Xlinker -load_hidden -Xliner “第二个静态库路径.a”
动态库 、静态库的实战
动态库与动态库的链接实战:

思路:  自己的framework,通过pod导入一个第三方,然后自己的项目,导入自己的framework并使用 ,反向使用等。

实现步骤:

1 .创建一个自己的framework (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的动态库  -> 第三方动态库 (AFNetworking) , 大致的动态库与动态库的模型就出来了。

后期上传demo.

主要出现的问题:

1. 运行app,找不到第三方库文件(因为这个第三方库,是动态库中所引用的第三方库),报错信息:

Undefined symbols for architecture x86_64:
“_OBJC_CLASS_$_ZGRAppObject”, referenced from:
objc-class-ref in ZGRAFNetworkingManager.o
解决方法是:在pod 文件中为这个target也加入pod 第三方库的方法,Podfile 代码如下:

platform :ios, ‘14.1’

#自己做的framework ,动态库
target :’ZGRNetworkManager’ do
use_frameworks!
pod ‘AFNetworking’
end

#比喻App
target :’ZGRNetworkManagerTests’ do
use_frameworks!
pod ‘AFNetworking’
end
2. 找不到对应的符号_OBJC_CLASS_$_ZGRAppObject ,  修改pod的配置文件,创建的target 和 动态库对应的配置文件都加上这句话。

OTHER_LDFLAGS = $(inherited) -framework “AFNetworking” -Xlinker -U -Xlinker _OBJC_CLASS_$_ZGRAppObject
动态库与静态库的链接实战:

思路:  自己的framework,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库。 (pods 只会生成链接器的参数,并不会把库文件放到framework下,pods是通过脚本是复制到指定目录,方式二:再使用的工程中,通过pods的配置文件,再指定加载的第三方库。)

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的动态库  -> 第三方静态库 (AFNetworking.a) , 大致的动态库与静态库的模型就出来了。

 

注:

动态库链接静态库,会链接所有的静态库符号, 所以当app去编译动态库的时候,并不会报错。

静态库的导出符号,也会出现在动态库符号里面,所以可以在app中直接使用,导入头文件即可。   (搜索不到,需要设置 header search paths路径 ${SRCROOT}/Pods/Headers/Public)

后期上传demo.

关于静态库的符号隐藏,通过在pod的配置文件加入如下代码:

OTHER_LDFLAGS = $(inherited) -Xlinker -hidden-l “AFNetworking”
 

静态库与静态库的链接实战:

思路:  自己的framework静态库组件,通过pod导入一个第三方静态库 AFNetworking.a ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework (静态库) (创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库静态库。

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的静态库组件  -> 第三方静态库 (AFNetworking.a) 。

 

操作记录:

1. 在静态库组件首先要设置 library search path ,  例如 : “${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking”

2. 设置Other Linker Flags , -lAFNetworking

 

原理就是: app去链接静态库组件, 但静态库组件所链接的静态库并没有路径链接到app,所以需要在组件中手动设置下所链接的静态库的路径与头文件。

 

静态库与动态库的链接实战:

公式:  app = app + 静态库 (所有的符号都是可见的)

思路:  自己的framework静态库,通过pod导入一个第三方动态库 AFNetworking ,在framework 中添加一个target,模拟app来使用 。

实现步骤:

1 .创建一个自己的framework ,静态库(创建方法见 : framework制作)

2. 在framework,通过cocoapods导入一个第三方库AFNetworking。

3. 打开我们自己的framework ,添加一个target,模拟成app,进行使用。

4. 现在的流程就是 app -> 自己的静态库  -> 第三方动态库 (AFNetworking) , 大致的静态库与动态库的模型就出来了。

 

Cocoapods 拷贝动态库脚本的用法。

首先,把脚本拷贝到工程根目录下,脚本文件,可以在任意cocoapods的工程中去找,  然后在项目中,在build phase 添加一个脚本,输入:

“${SRCROOT}/Pods-LGNetworkManagerTests-frameworks.sh”
这样在运行项目的时候,脚本会自动将动态库拷贝到product目录中。

 

总结:

1. XCFramework  – sdk开发,组件开发

解决了:

1.头文件的处理

2. 调试符号的保存

3. 相同架构的处理

4.2019年才出。

 

2.  实战

1. weak_import

使用场景:  动态库 运行时 -》 不能确认到指定位置,所以用到了弱引用,当找不到动态库的时候,项目不会报错的作用

2. 静态库冲突 -》 app -> all_load\-Objc

3. app – 动态库 – 动态库链接  ,app不知道所以需要以下操作

方式一: 通过cocoapod脚本复制

方式二: 通过cocoapod 加载第三方库,并不会加载两次。 不用担心

 

动态库 – 静态库

存在问题:

静态库不想暴露到处符号,需要用到 -hidden -l静态库

 

app – 静态库 – 静态库

1. 名称  知道。

2 不知道所在位置: 所以只需要告诉位置

 

app – 静态库 – 动态库

1. 编译报错:  不知道动态库的路径

2. 运行报错: 不知道动态库的rpath

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速