Zxl的技术blog
OC汇编知识整理
汇编语言之ARM64汇编
str(store register)指令
从寄存器中读数据,存到内存中
ldr(load register)指令
从内存中读数据,存到寄存器中
此ldr 和 str 的变种ldp(pair) 和 stp(pair) 还可以操作2个寄存器
stur指令: 偏移量为减时使用 . stur w0, [x29, #-0x8] 偏移量为负的 将寄存器w0的值存入x29 - 0x8 的内存地址
stp w0,w1,[x2] //把w0和 w1里面的值,写入到右边内存,[x2]中,w0在左边,w1在右边
str w0 [x2] /把w0的值写入到右边内存,[x2]中
ldp [x2], w0, w1
ldr [x2], w0
adrp(address page):地址页
用于计算指定数据所在物理地址和当前pc地址之间的偏移量, 也就是说通过该指令计算出常量的物理地址
adrp x0,1
;1.将1的值左移12位, 1 0000 0000 0000 ==0x1000
;2.将pc寄存器的低12位清零
;3.清零之后的值加上0x1000 然后将最后结果赋值给x0寄存器
;adrp指令后边的数值1为十六进制
add/sub
目的寄存器,操作数1,操作数2
SUB R0,R1,R2 // R0 = R1 - R2
bl指令
执行方法,类似于x86汇编中的, call
ARM64提供了另外的指令来修改PC的值,这些指令统称为转移指令,最简单的是bl指令
xzr/wzr (ARM64 zero register)
ARMv8 在硬件层名引入了一个新的 zero 寄存器:XZR(64-bits), WZR(32-bits)。比如要将某一变量赋值为0x0,
由于ARM不允许直接操作内存单元上的数据就,所以需要先将一个寄存器置0,然后再将这个寄存器的值store到内存单元上,如下:
ldr x1, =0x18ac0000
mov w0, #0
str w0, [x1, #0]
现在有有了zero 寄存器,那么一条指令就可以解决上面需要两条指令才能解决赋值0的问题:
str wzr, [x1, #0]
GL知识点梳理
*、位bit(二进制01),1字节byte = 8 bit(0-255)
*、每种颜色8bit,每像素总共4x8bit即4byte (drawableColorFormat颜色格式GLKViewDrawableColorFormatRGBA8888,
如果允许也有RGB56这种更小范围的颜色消耗更小资源)
*、在iOS的不同framework中使用着不同的坐标系
UIKit - y轴向下,
Core Graphics(Quartz) - y轴向上,
OpenGL ES - y轴向上
UIGraphicsBeginImageContextWithOptions拿到context坐标轴是修正过的,方向跟uikit一致
CGBitmapContextCreate出来的context方向是y轴向上,跟uikit相反
CGContextDrawImage绘制是按照Quartz的y轴向上来画的
uiImage drawInRect会修正方向,跟uikit一致
iOS编译原理
iOS编译原理
基本的编译过程
1.预处理(Pre-process):把宏替换,删除注释,展开头文件,产生.i文件。
2.编译(Compliling):把之前的.i文件转换成汇编语言,产生.s文件。
3.汇编(Asembly):把汇编语言文件转换为机器码文件,产生.o文件。
4.链接(Link):对.o中的对其他库的引用的地方进行引用,生成最后的可执行文件(同时包括多个.o进行 link)
clang、swiftc是编译前端,llvm编译后端
编译器前端:语法分析,语义分析,类型检查,生成中间代码AST(语法树)。
编译器后端:中间代码优化,生成目标代码(机器语言),目标代码优化。
Swift代码-->通过swiftc生成Swift AST(Swift语法树)-->Raw Swift IL(Swift特有的中间代码)
-->Canonical Swift IL(更简洁的Swift代码)-->转交给后端生成LLVM代码-->Assembly(汇编代码)-->Executable(二进制代码)
执行一次XCode build的流程:
· 编译信息写入辅助文件,创建文件架构(name.app)
· 处理文件打包信息,例如在debug环境下
Entitlements:{ "application-identifier" = "app的bundleid"; "aps-environment" = development;}
· 执行CocoaPod编译前脚本(CheckPods Manifest.lock)
· 编译各个.m文件,使用CompileC和clang命令
· 链接Framework
·编译xib文件·拷贝xib,图片等资源文件到结果目录·编译ImageAssets
·处理info.plist
·执行CocoaPod脚本
·拷贝Swift标准库
·创建.app文件和对其签名
提高项目编译速度
1、.h文件用@class不用import
2、常用功能、工具类打包成framework或者.a
3、常用头文件放到预编译文件里pch. 在XCode build前就已被预编译,并引入到每个.m里了
4、Debug模式下,不生成dsym文件(Debug Information Format -> debug只用DWARF,release用DWARF with dSYM File)
(.dysm文件里存了调试信息)
5、Debug开启Build Active Architecture Only.这样只编译当前的版本,比如arm7/arm64等等
LLVM 编译器和工具技术的集合,*优化编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),
*模块化、语言无关的中间代码、工具和函数库(优化/调试特性)
在函数调用bl或者blr指令处设置断点后,因为根据ABI规则所有非浮点数的参数分别依次保存在x0,x1,....这些寄存器中。
所以可以在断点处打印
(lldb)po $x0
<__NSArrayM 0x1c044c2a0>(
/Library/Developer/Xcode/Archives目录下
当我们软件release模式打包或上线后,iOS设备中会有日志文件保存我们每个应用出错的函数内存地址,
通过Xcode的Organizer可以将iOS设备中的DeviceLog导出成crash文件,这个时候我们就可以通过出错的函数地址去
查询dSYM文件中程序对应的函数名和文件名。大前提是我们需要有软件版本对应的dSYM文件,这也是为什么我们很有必要保存
每个发布版本的Archives文件了。
每一个xx.app和xx.app.dSYM文件都有对应的UUID,crash文件也有自己的UUID,
只要这三个文件的UUID一致,我们就可以通过他们解析出正确的错误函数信息了。
DWARF 是一种调试信息格式
iOS显示原理
离屏渲染
iOS图像与性能:图像显示,事件传递,性能优化,离屏渲 https://www.cnblogs.com/cleven/p/12750545.html
代码布局 可以在loadview中写self.view = xxx;不需要调用[super loadview],因为super里面做的事情就是创建一个空白view,
frame是[uiscreen mainscreen].bounds(以前是applicationframe,出去了状态栏,弃用了),调用super会创建一次,自己如果再赋值,
之前的创建就多余了,但是如果自己也不给self.view赋值,也不调用[super loadview],就会没有self.view,报错
所以二者有一个就ok
iOS 图像渲染原理
http://www.cocoachina.com/cms/wap.php?action=article&id=25510
coretext 绘制原理 https://www.jianshu.com/p/e0277ac633bc
view的绘制和runloop的关系
当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法
后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。
苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,
回调去执行一个很长的函数_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。
这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面
关于SKAdNetwork和AdAttributionKit的调研
苹果广告相关SKAdNetwork和AdAttributionKit
1. TLDR
一句话,AdAttributionKit基于SKAdNetwork,两者都是用于具有量能的广告平台商测量和归因广告投放效果的框架,
而非是用于监测自家应用安装渠道的工具。
2. 术语表
- 广告平台商 比如pangle(AdAttributionKit下视作AppStore本身就是广告平台且所有经过AppStore的应用在同一网络)
- 广告投放者 比如要通过广告平台商推广自家应用的
- 网络 不同的网络id分别是一个网络
3. 为什么会有SKAdNetwork
因为idfa被废了,用户可以不给idfa,那么广告主就没法知道广告点击设备和新安装应用设备为同一设备。
4. SKAdNetwork可以做什么
SKAdNetwork可以在同一网络下应用间建立推广-安装事件链条,并在数据和时间尺度上进行大量模糊,并最终发给广告平台商,
进而让广告投放者量化广告投放效果
5. 什么是网络
在应用info.plist内注册的SKAdNetworkItems下的键值对数组SKAdNetworkIdentifier对应的网络id为一个网络,
注册有同一个网络id的应用在同一个网络
6. 事件链条
1.广告平台商从实力的地位出发小【展示广告的应用A】和【广告投放者的应用B】注册同一网络id,加入同一网络
2.【展示广告的应用A】展示广告并且用户触发了某种广告形式,并调用了-[SKStoreProductViewController loadProductWithParameters …]
方法展示了【广告投放者的应用B】
3.用户安装了【广告投放者的应用B】后,应用调用了-[SKAdNetwork registerAppForAdNetworkAttribution]方法通知网络发生了【安装事件】
4.用户在【广告投放者的应用B】进一步触发了别的注册付费完成关卡之类的操作,应用调用-[SKAdNetwork updatePostbackConversionValue: …]
方法并在6个bit总计64个转化状态中选一个通知网络发生了【转化事件】
5.在苹果对数据进行模糊化过滤后(不限于内容过滤、通知时间随机化延迟),苹果向网络id后台发送该事件数据
7. 广告平台商能得到哪些数据
{
"version": "4.0",
"ad-network-id": "example.app",
"campaign-id": 42,
"transaction-id": "abcd-1234-efgh-5678",
"app-id": 123456789,
"attribution-signature": "ABC123...",
"redownload": false,
"source-app-id": 987654321,
"conversion-value": 3,
"coarse-conversion-value": "medium",
"postback-sequence-index": 0,
"fidelity-type": 1
}
1.广告是怎么触发的
2.展示广告的app
3.分级别2-4位(decimal)的活动id
4.用户是否是第一次安装
5.转化事件状态0-63
8. AdAttributionKit能做什么
相当于苹果作为最大的广告商,把所有应用拉进同一个网络,监测走AppStore广告推广投放的事件,其他和SKAdNetwork一样
iOS项目重构备忘录
搞清楚项目重构目的:
稳定性?速度?安全性?负载?bug?可读性?可维护性?扩展性?解耦?复用?
统一代码规范
整理目录、资源文件
基础库统一、分离功能模块、代码文件梳理重构
三方框架更新
安全分析,source文件加密
*防止过度设计:
胶水代码过多
大量文件的行数小于50/100
拆分过细,阅读时跳转太多
iOS开发之Xcode删除项目中未用到的OC头文件和方法 https://www.jianshu.com/p/e79da45af303
找出OC项目中未用到OC的头文件引用 https://github.com/dblock/fui
检测出objc项目中无用的方法 https://github.com/ming1016/SMCheckProject
*oc里面能用成员变量尽量不用属性,必然第一时间用到的不使用懒加载
*、非单例的类里面使用信号量量 ,多线程中如果释放 ,可能会导致闪退(一段涉及信号量的代码执行中 ,
另一个 线程 让这个实例释放了,信号量也释放了,之前那段代码再执行到信号量的地方就会闪退 )
*、 NSLog在release下禁用,可以用宏定义
*、宏定义与常量 https://blog.csdn.net/wqs1028/article/details/50370991
可以用常量的尽量少用宏
关于const常量,https://blog.csdn.net/jiqiren007/article/details/6213778
1、c语言中全局变量和static变量的初始化需要指定一个常量,不能是一个非常量的表达式;
而在c++中(xcode选择objc ++编译的情况下)是可以的(编译器做了优化)
比如UIColor * const kThemeColor = [UIColor xxx];在常规oc编译会报错,oc++编译就可以通过
**在ipad上跑iphone项目的时候,某些特殊情况会导致刚启动的时候的screensize尺寸还是ipad的尺寸,
不是模拟出的iphone的尺寸,所以如果const常量赋值时用了kscalex,会拿到错误的值,所以还是不要kscalex还是不要给const赋值
(拿到错误的尺寸是因为某个类的load方法里面左右一些不当操作,导致的)
2、在操作c和c++全局变量时,只能对其采用初始化的方式,而不能采用赋值的方式
也就是就说做为全局变量,在编译之后就要确定它的地址,如果这个全局变量是个结构体的话,那么结构体里面的各个成员的具体值也要确定了。
因为全局变量是存储在静态区,而只有常量表达式才可以前期确定值,而不是具体运行时确定值。这么说g++之所以可以编译通过,
那是它的链接器有优化,做了后期绑定。
UIKIT_EXTERN 与 extern 的区别,简单来说就是如果要确保库外面也能访问这个常量,则用UIKIT_EXTERN
https://blog.csdn.net/victor_barnett/article/details/50618119
*、无用代码直接删除,尽量不要注释掉,要看老版本代码可以用git工具看(实在要注释必须文字说清楚注释掉的这段代码的作用,
留在这儿而不是删掉的原因,后面还是要找时间删除)
*、可以用工具检测未使用的资源和类,进行删除
查未用类 https://www.jianshu.com/p/d6088e2e3f5d
查未用资源 https://www.jianshu.com/p/5d9c5d03d462
*、memorywarning 清理相关内存,比如缓存,比如静态vc等,否则在后台极容易被kill
*、内存泄漏(CGImageRef,block循环引用等)
dealloc打印消息
leaks、allocations
cgimage的使用必须自己释放,比如拿出视频第一帧的图片
CGImageRef thumbnailImageRef = [imageGenerator copyCGImageAtTime:CMTimeMake(0, 600) actualTime:nil error:nil],
thumbnailImageRef使用完后需要调用CGImageRelease(xxx);效果同cfrelease
*设阴影要预设shadowpath,减少离屏渲染时间
[myView.layer setShadowPath:[[UIBezierPath bezierPathWithRect:myView.bounds] CGPath];
*、dataformater 少用
*、网络
封装、安全策略、缓存策略
优化策略-dns、gzip、http协议版本等
三方框架:
avoidcrash使用
监测工具使用(或者自己写)
*启动优化
editscheme将 DYLD_PRINT_STATISTICS 设为1可查看启动中pre-main的时间
quicik build的target:
Total pre-main time: 1.6 seconds (100.0%)
dylib loading time: 63.70 milliseconds (3.9%)
rebase/binding time: 352.17 milliseconds (21.6%)
ObjC setup time: 315.22 milliseconds (19.4%)
initializer time: 891.69 milliseconds (54.9%)
slowest intializers :
libSystem.B.dylib : 13.28 milliseconds (0.8%)
libMainThreadChecker.dylib : 36.71 milliseconds (2.2%)
libglInterpose.dylib : 258.33 milliseconds (15.9%)
libMTLInterpose.dylib : 50.65 milliseconds (3.1%)
FaceBeautyV1PLUS : 885.51 milliseconds (54.5%)
*删除无用动态库(未用到或者功能重复的)
rebase优化,指针数量越少越好:
*减少ObjC类、方法、category的数量(静态库)(未用到或功能重复的)(换用轻量级库)
比如oc里面能用成员变量尽量不用属性,必然第一时间用到的不使用懒加载
*减少C++虚函数的的数量(创建虚函数表有开销)
其他初始化阶段:
*少在类的+load方法里做事情,尽量把这些事情推迟到+initiailize(交换方法需要做判断只交换一次)
*减少C/C++构造器函数个数,在构造器函数里少做些事情
*减少C++静态全局变量的个数
main函数UIApplicationMain过程:
*尽量延迟加载可延迟加载的库;延迟做一些逻辑处理;一些操作放到分线程;
vc的loading时可以返回,返回时取消当前页面的非空请求(开始请求保存请求task,请求结束置空该task,因取消请求的失败回调不做用户提示)
区别于fullscreen的loading,返回点击无效
*滤镜收藏是前面多了一份收藏,导致item变多,显示有后移,用户体验不好
联系邮箱
07122003@163.com
Copyright 2025 ZXL. All rights reserved.
苏ICP备17039570号-1