1. LLDB调试打印
2. UIWebView设置UA
3. 预处理宏,Preprocessor Macros
4. 快捷键
5. 执行xcodebuild命令 不工作 xcodebuild not work , 输出 Build settings from command line: 命令后停止。用Xcode打开后就可以执行 编译命令了。
6. iOS 8.3之后获取iOS状态栏上手机的信号强度和wifi信号强度Signal Strength 。上架appstore会被拒,混淆后不知道可不可以
7. 打印出程序启动过程中各个阶段所消耗的时间
8. shell 脚本 选择编译Xcode ,然后在终端或者编译机上执行脚本就好。用于Xcode切换
9. 调用系统相册、相机,发现是英文的标题“photos”,但是手机语言已经设置显示中文。
10. Autolayout 实现UILablel文字竖排
11. plist 文件的格式转换 二进制或者 xml
12. brew的一些命令
13. CocoaPods 引入测试库
14. UIViewController 设置导航栏和标签栏不同 title 的问题
15. VC 多层 push 后回到指定页面的几种方法
16. "互斥锁" 避免死锁
17.Debug Memory Graph 应用 检测内存泄漏
18. heap WeChat | egrep 'class_rw|COUNT' 查看 class_rw_t 消耗的内存
19. nm BUAdSDK | grep crash_handle_signal 查看 Mach-O 中是否包含 crash_handle_signal 符号
20. sudo killall -STOP -c usbd iOS手机usb链接一断一连一断一连,执行该命令
21.otool -ov 输出Objective-C类结构及其定义的方法
22. strings mach-o 在对象文件或者二进制文件中查找可打印的字符串
23. ```SwiftUI 用的 通过开启 AG 的相关环境变量(AG_PRINT_CYCLES=2
& AG_TRAP_CYCLES=1
),我们可以获取更多信息并让其在发生循环处断言崩溃。``` 目前还不知道干啥用的
24. Unmanaged.passUnretained(self).toOpaque() Swift 打印对象地址
25. Build Settings -> Other Swift Flags 添加
-Xfrontend
-warn-long-function-bodies=<milliseconds>
-Xfrontend
-warn-long-expression-type-checking=<milliseconds>
可在 Xcode 警告看哪些函数哪些类型检查编译耗时太长。<milliseconds> 一般填写 50
26. 获取 Swift 编译器的中间产物
27. 利用 LLVM 传递参数
28. SIL 和 LLVM IR 的语法高亮
1. LLDB调试打印,断点一个UIPasteboard的所有方法
(lldb) p UIScreen.mainScreen.bounds
error: property 'bounds' not found on object of type 'id'
error: 1 errors parsing expression
(lldb) expr @import UIKit
(lldb) p UIScreen.mainScreen.bounds
(CGRect) $1 = (origin = (x = 0, y = 0), size = (width = 414, height = 736))
(lldb)
断点一个UIPasteboard的所有方法可以用如下命令
breakpoint set -r '\[UIPasteboard .*\]$'
br list
用lldb指令 frame variable -R 或者fr v -R查看对象结构
2.UIWebView设置UA
@implementation lzxzWebViewUtility : NSObject
+ (id)uiWebDocumentView:(UIWebView *)webview
{
NSString *str = @"_documentView";
id documentView = [webview performSelector:NSSelectorFromString(str)];
id coreWebView = [documentView performSelector :@selector(webView)];
return coreWebView;
}
+(NSString *)langzuxiaoziUA:(UIWebView *)webview
{
static NSString* lbUA=nil;
NSString* appUA=objc_msgSend([lzxzWebViewUtility uiWebDocumentView:webview], NSSelectorFromString(@"applicationNameForUserAgent"));
if(lbUA == nil){
lbUA=[[appUA stringByAppendingFormat:@" langzuxiaozi/%@",[lzxzUIGlobal appBuildNum]] retain];
}
return lbUA;
}
+ (void)changeWebview:(UIWebView *)webview UserAgent:(NSString*)strUA
{
SEL sel = NSSelectorFromString(@"setApplicationNameForUserAgent:");
[[lzxzWebViewUtility uiWebDocumentView:webview] performSelector:sel withObject:strUA];
}
@end
//获取到带langzuxiaozi字符的ua
NSString* strUA = [lzxzWebViewUtility langzuxiaoziUA:self.webView];
//设置webview自定义ua
[lzxzWebViewUtility changeWebview:self.webView UserAgent:strUA];
3.预处理宏,Preprocessor Macros
如果写一些打印日志,只希望在debug版本中打印,release版本中不打印,可以这么写
#ifdef DEBUG
NSLog(@"%s",__func__);
#endif
这样程序在编译的时候,只有在debug版本中才会编译这段代码。DEBUG是由Xcode创建项目的时候自动添加的配置,位置在Project -> Build Settings -> Preprocessor Macros 的Debug中。
也可以自定义一些其他条目到debug或release中
4. 快捷键
项目之间切换 command + ~
.h和.m之间切换 control + command + ↑ or ↓
定位类文件 command + shift + J
类文件查找command+ shift + O
方法调用者查看 光标 放到函数名上 control+1 然后鼠标选Callers
5. 执行xcodebuild命令 不工作 xcodebuild not work , 输出 Build settings from command line: 命令后停止。用Xcode打开后就可以执行 编译命令了。
xcodebuild clean build -workspace osvideo.xcworkspace -configuration "Release" -scheme osvideo -archivePath /Users/ios/.hudson/jobs/Video.ly_test/workspace/build/archivefiles.xcarchive -sdk iphoneos archive
通过对比刚刚checkout 出的代码和 用Xcode打开后的代码的区别 发现在 projectName.xcworkspace projectName.xcodeproj 这中文件夹里有xcuserdata 文件夹,xcuserdata文件
夹里 有 username.xcuserdatad. 只要多了这个username.xcuserdatad文件夹,就能通过命令行编译。
解决办法是在 product -> scheme -> Manage Schemes 中的所有 scheme 右边的 Shared 全部选中。
这样后,重新checkkout 下来的代码就可以直接用命令行编译了
6.iOS 8.3之后获取iOS状态栏上手机的信号强度和wifi信号强度Signal Strength 。上架appstore会被拒,混淆后不知道可不可以
- (IBAction)btn:(id)sender {
UIApplication *app = [UIApplication sharedApplication];
NSArray *subviews = [[[app valueForKey:@"statusBar"] valueForKey:@"foregroundView"] subviews];
NSString *dataNetworkItemView = nil;
NSString *dataSignalStrengthView = nil;
for (id subview in subviews) {
if([subview isKindOfClass:[NSClassFromString(@"UIStatusBarDataNetworkItemView") class]])
{
dataNetworkItemView = subview;
}
if([subview isKindOfClass:[NSClassFromString(@"UIStatusBarSignalStrengthItemView") class]]){
dataSignalStrengthView = subview;
}
}
if(dataNetworkItemView){
int wifiStrengthRaw = [[dataNetworkItemView valueForKey:@"wifiStrengthRaw"] intValue];
int wifiStrengthBars = [[dataNetworkItemView valueForKey:@"wifiStrengthBars"] intValue];
int dataNetworkType = [[dataNetworkItemView valueForKey:@"dataNetworkType"] intValue];
self.textView.text = [NSString stringWithFormat:@"wifiStrengthRaw = %d \nwifiStrengthBars = %d\n dataNetworkType = %d",wifiStrengthRaw,wifiStrengthBars,dataNetworkType];
}else{
self.textView.text = @"";
}
if(dataSignalStrengthView){
int signalStrengthRaw = [[dataSignalStrengthView valueForKey:@"signalStrengthRaw"] intValue];
int signalStrengthBars = [[dataSignalStrengthView valueForKey:@"signalStrengthBars"] intValue];
self.textView.text = [NSString stringWithFormat:@"%@\nsignalStrengthRaw = %d\nsignalStrengthBars = %d",self.textView.text,signalStrengthRaw,signalStrengthBars];
}
}
7. Edit Scheme ->Arguments -> Environment Variables 添加 DYLD_PRINT_STATISTICS = 1,
这样在调试时,可以在控制台打印出程序启动过程中各个阶段所消耗的时间。
8.shell 脚本 选择编译Xcode ,然后在终端或者编译机上执行脚本就好。用于Xcode切换
#!/bin/bash
echo 'pwd'|sudo -S xcode-select -s /Applications/Xcode7.3.1.app
9.调用系统相册、相机,发现是英文的标题“photos”,但是手机语言已经设置显示中文。
打开“info.plist”,添加一项“Localized resources can be mixed”,值设置为“yes”即可!
10.Autolayout 实现UILablel文字竖排
UILabel *label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:16];
label.preferredMaxLayoutWidth = label.font.pointSize;
不要设置UILabel宽度约束
11.plist 文件的格式转换 二进制或者 xml
plutil -convert binary1 /Users/wolf/code/Keyboard/keyboard_layout_standard_dvorak.plist -o inof.plist
这是转换成二进制文件.要转换成 xml 文件,就把 binary1 换成 xml
12. brew的一些命令
brew outdated 查看你的包是否需要更新
brew list --versions 查看你安装过的包列表
brew --version 查看brew版本
brew update 更新brew
brew upgrade 更新包(所有的)
brew upgrade swxftlint 更新某个包,比如swxftlint包
brew cleanup 清理旧版本的包缓存(Homebrew 将会把老版本的包缓存下来)
13.CocoaPods 引入测试库
通过 CocoaPods 引入的第三方库可以通过配置 configurations 选项让它们只在 Debug 下生效
pod 'netfox', :configurations => ['Debug']
14.UIViewController 设置导航栏和标签栏不同 title 的问题
在一个 VC 中设置相关标题简单总结如下:
self.navigationItem.title: 设置 VC 顶部导航栏的标题
self.tabBarItem.title: 设置 VC 底部标签栏的标题
self.title: 同时修改上述两处的标题
参考文档
UIViewController.title
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621364-title?language=objc
15. VC 多层 push 后回到指定页面的几种方法
RootVC -- > A -- > B -- > C,然后现在要求C直接pop回到A。
方法一:C返回到B的时候写个回调,B接收到回调再自己pop到A,但是这个方法B的页面会闪现一下,用户体验不好,不推荐。
方法二:在B push 到C的时候,直接把B从导航控制器的堆栈中移除
C_ViewController *vc = [[C_ViewController alloc] init];
NSMutableArray *navArray = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
[navArray replaceObjectAtIndex:[navArray count] - 1 withObject:vc];
[self.navigationController setViewControllers:navArray animated:YES];
方法三:写一个UIViewController的catrgory,方法实现如图二。在C的backAct方法中使用,如图三。有的同学可能会怀疑B会不会内存泄露,可以在B中打印dealloc。
- (void)backToController:(NSString*)ctrlClassName animated:(BOOL)animated{
if (self.navigationController){
NSArray *controllers = self.navigationController.viewControllers;
NSArray *result = [controllers filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return [evaluatedObject isKindOfClass:NSClassFromString(ctrlClassName)];
}]];
if (result.count > 0){
[self.navigationController popToViewController:result[0] animated:animated];
}
}
}
- (void)backAct{
[self backToController:@"A_ViewController" animated:YES];
}
这里比较推荐方法三。不论有多少级的push,只要传入指定页面的类名,都能回到该页面。
16. "互斥锁" 避免死锁
看 YYMemoryCache.m 的源码.
pthread_mutex_t _lock;
pthread_mutex_init(&_lock, NULL);
BOOL finish = NO;
while (!finish) {
if (pthread_mutex_trylock(&_lock) == 0) {
if (_lru->_totalCount > countLimit) {
_YYLinkedMapNode *node = [_lru removeTailNode];
if (node) [holder addObject:node];
} else {
finish = YES;
}
pthread_mutex_unlock(&_lock);
} else {
usleep(10 * 1000); //10 ms
}
}
pthread_mutex_destroy(&_lock);
重点是使用 pthread_mutex_trylock()
方法尝试获取锁,而获取失败过后做了一个线程睡眠操作usleep().
在老版本中用是
自旋锁 OSSpinLock,
后来由于OSSpinLock
的 bug 问题(存在潜在的优先级反转BUG),作者将其替换成了pthread_mutex_t
互斥锁。
互斥锁它有一个特性:当多个线程出现数据竞争时,除了“竞争成功”的那个线程外,其他线程都会空转一段时间后进入被动挂起状态,而当“竞争成功”的那个线程解锁时,会主动去将其他线程激活,这个过程包含了上下文的切换,cpu抢占,信号发送等开销,很明显,互斥锁的起始开销有些大,效率低于自旋锁.
pthread_mutex_trylock() 尝试获取锁,获取失败会立刻返回,不会空转然后被挂起.
在当前线程睡眠10毫秒,然后再次尝试获取锁.这么做多了线程切换的时间.效率下降了吧.
17. Debug Memory Graph 应用 检测内存泄漏
为了能看到内存详细信息,先打开 Edit Scheme-->Diagnostics, 勾选 Malloc Scribble 和 Malloc Stack。为了避免过多的性能消耗,在 Malloc Stack 中直接选择 Live Allocations Only 即可。
然后运行Xcode,点击三个圆圈的小按钮后,右边栏点击右上角的 Show the Memory Inspector,会有堆栈信息,并且能直接定位到内存泄漏的代码块
21.otool -ov 输出Objective-C类结构及其定义的方法
0000000103bb9100 0x1045abe10 _OBJC_CLASS_$_WeatherData
isa 0x1045abe38 _OBJC_METACLASS_$_WeatherData
superclass 0x0 _OBJC_CLASS_$_NSObject
cache 0x0 __objc_empty_cache
vtable 0x0
isa 0x1045abe38 _OBJC_METACLASS_$_WeatherData
superclass 0x0 _OBJC_CLASS_$_NSObject
cache 0x0 __objc_empty_cache
vtable 0x0
data 0x103d81130 __OBJC_CLASS_RO_$_WeatherData
flags 0x184 RO_HAS_CXX_STRUCTORS
instanceStart 8
instanceSize 408
reserved 0x00000000
ivarLayout 0x103935184
layout map: 0x33 0x17 0x1f 0x0f 0x32
name 0x103935178 WeatherData
baseMethods 0x103d7fba0 __OBJC_$_INSTANCE_METHODS_WeatherData
entsize 24
count 119
name 0x103771a5f init
types 0x10395b692 @16@0:8
imp 0x1009a48d4 -[WeatherData init]
name 0x1037c4ff6 isExpired
types 0x10395b6d7 B16@0:8
imp 0x1009a49e8 -[WeatherData isExpired]
name 0x1037e4027 isGPSExpired
types 0x10395b6d7 B16@0:8
imp 0x1009a4acc -[WeatherData isGPSExpired]
name 0x103778381 sceneID
types 0x10395b692 @16@0:8
imp 0x1009a4bb0 -[WeatherData sceneID]
26. 获取 Swift 编译器的中间产物
swiftc 1.swift -Osize -emit-silgen > 1.silgen.sil # 生成原始 SIL
swiftc 1.swift -Osize -emit-sil > 1.sil.sil # 生成优化 SIL
swiftc 1.swift -Osize -emit-irgen > 1.irgen.ll # 生成原始 LLVM IR
swiftc 1.swift -Osize -emit-ir > 1.ir.ll # 生成优化 LLVM IR
// 分析输出AST
swiftc main.swift -dump-parse
// 分析并且检查类型输出AST
swiftc main.swift -dump-ast
// 生成中间体语言(SIL),未优化
swiftc main.swift -emit-silgen
// 生成中间体语言(SIL),优化后的
swiftc main.swift -emit-sil
// 生成LLVM中间体语言 (.ll文件)
swiftc main.swift -emit-ir
// 生成LLVM中间体语言 (.bc文件)
swiftc main.swift -emit-bc
// 生成汇编
swiftc main.swift -emit-assembly
// 编译生成可执行.out文件
swiftc -o main.o main.swift
// 通过命令xcrun swift-demangle还原成原来的名字
swiftc -emit-sil main.swift | xcrun swift-demangle
// 类中包含 UIKIt SDK,输出 sil
swiftc -emit-sil -target x86_64-apple-ios14.2-simulator -sdk $(xcrun --show-sdk-path --sdk iphonesimulator) ViewController.swift > ViewController.sil
27. 利用 LLVM 传递参数
# 打印指定函数的 SIL 更改
swiftc -Xllvm '--sil-print-function=$MangledSwiftFunctionName'
# 在运行每个 SIL 过程前打印其名称
swiftc -Xllvm '--sil-print-pass-name=pass-name'
# 打印内联到其他函数中的函数
swiftc -Xllvm '--sil-print-inlining-callee=true'
28.SIL 和 LLVM IR 的语法高亮
在 VS Code 扩展市场中搜索 "WeZZard" 以获取相关 IDE 中的 SIL 语法高亮。
在 VS Code 扩展市场中找到 Ingvar Stepanyan 的 "LLVM" 扩展,以获取相关 IDE 中的 LLVM IR 语法高亮。