跳到主要内容

7 篇博文 含有标签「开发技巧」

查看所有标签

利用 Debug Memory Graph 检测内测泄漏

· 阅读需 2 分钟
BY

前言

平常我们都会用 Instrument 的 Leaks / Allocations 或其他一些开源库进行内存泄露的排查,但它们都存在各种问题和不便,

在这个 ARC 时代更常见的内存泄露是循环引用导致的 Abandoned memory,Leaks 工具查不出这类内存泄露,应用有限。

今天介绍一种简单直接的检测内测泄漏的方法:Debug Memory Graph

就是这货:

正文

我最近的项目中,退出登录后(跳转到登录页),发现首页控制器没有被销毁,依旧能接收通知。

退出登录代码:

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Login" bundle:[NSBundle mainBundle]];
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
appDelegate.window.rootViewController = [storyboard instantiateViewControllerWithIdentifier:@"LoginVC"];

很明显发生了循环引用导致的内测泄漏。

接下来就使用 Debug Memory Graph 来查看内测泄漏了。

运行程序

首先启动 Xcode 运行程序。

Debug Memory Graph

点击 Debug Memory Graph 按钮后,可以看到红框内的是当前内存中存在的对象。其中,绿色的就是视图控制器。

这样,我们随时都可以查看内测中存在的对象,换句话说,就是可以通过观察 Memory Graph 查看内测泄漏。

调试你的App

继续运行你的程序

然后对App进行调试、push、pop 操作,再次点击 Debug Memory Graph 按钮。那些该释放而依旧在内测中的 控制器对象 就能一一找出来了。

接下来,只要进入对应的控制器找到内测泄漏的代码就OK了,一般是Block里引用了 self,改为 weakSelf 就解决了。

#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self;

WS(weakSelf)
sView.btnBlock = ^(NSInteger idx){
[weakSelf.tableView reloadSections:[NSIndexSet indexSetWithIndex:idx] withRowAnimation:UITableViewRowAnimationAutomatic];
};

结语

就这样,利用 Debug Memory Graph,可以简单快速的检测内测泄漏。

一般由两个对象循环引用的内测泄漏是比较好发现的,如果是由三个及其三个以上的对象形成的大的循环引用,就会比较难排查了。

Xcode9 无线调试功能

· 阅读需 1 分钟
BY

支持:Xcode 9 及 iOS 11

使用数据线连接 iPhone 到电 Mac,Mac 和 iPhone 必须在同一个局域网

1. 打开设备列表

使用快捷键盘 ⇧⌘2 或 在 Xcode 菜单栏选择 Window > Devices and Simulators,打开设备列表

2. 勾选在线调试按钮

3. 拔掉数据线

这时就可以无线调试了。

文件目录树状(tree)显示

· 阅读需 1 分钟
BY

使用 tree 在终端显示树状文件结构

安装 tree

使用 brew 进行安装

$ brew install tree

使用

  • 直接使用 tree 命令,会在当前文件目录下,递归输出所有文件层级

    $ tree

  • 限制层级

    $ tree -L 2

  • 指定当前目录下的某个文件夹

    $ tree Desktop

导出文件

> 文件名.格式 的形式导出

$ tree -L 1 > tree.md

Mac 快速调出终端

· 阅读需 2 分钟
BY

在Mac下快速调出终端的方法是:为终端添加一个快捷键打开方式

为终端添加一个快捷键打开方式

打开Mac下自带的软件 Automator

新建文稿

创建一个服务

修改框内的脚本

on run {input, parameters}
tell application "Terminal"
reopen
activate
end tell
end run

运行:command + R,如果没有问题,则会打开终端

保存:Command + S,将其命名为打开终端或你想要的名字

设置快捷键

系统偏好设置 -> 键盘设置 -> 快捷键 -> 服务

选择我们创建好的 '打开终端',设置你想要的快捷键,比我我设置了⌘+空格

到此,设置完成。

聪明的你也许会发现,这个技巧能为所有的程序设置快捷启动。

将脚本中的 Terminal 替换成 其他程序就可以

on run {input, parameters}
tell application "Terminal"
reopen
activate
end tell
end run

黑技能

既然学了 Automator ,那就在附上一个黑技能吧。为你的代码排序。在 Xcode8以前,有个插件能为代码快速排序,不过时过境迁~ 对于没用的插件而且又有患有强迫症的的小伙伴,只能手动排序了(😂).

首先还是创建一个服务

创建一个Shell脚本,

勾选:用输出内容替换所选文本

输入:sort|uniq

保存: 存为Sort & Uniq

选中你的代代码 -> 鼠标右键 -> Servies -> Sort&Uniq

排序后的代码:

快速添加圆角和描边

· 阅读需 3 分钟
BY

对于习惯使用Storyboard的人来说,设置圆角、描边是一件比较蛋疼的事,因为苹果没有在xcode的Interface Builder上直接提供修改控件的圆角,边框设置。

我们来说说如何对某个控件进行圆角、描边处理:

初级

对于一个初学者来说,如果要进行某个控件的圆角、描边设置,就要从Storyboard关联出属性,然后再对属性进行代码处理。

如下代码:

self.myButton.layer.cornerRadius = 20;
self.myButton.layer.masksToBounds = YES;
self.myButton.layer.borderWidth = 2;
self.myButton.layer.borderColor = [UIColor blackColor].CGColor;

这样不仅需要Storyboard关联出属性,还要写一堆代码对属性进行设置,不得不说实在麻烦~

中级

更聪明的做法是使用Storyboard提供的Runtime Attributes为控件添加圆角描边。

选中控件,然后在Runtime Attributes框中输入对应的KeyTypeValue,这样程序在运行时就会通过KVC为你的控件属性进行赋值。(不仅仅是圆角、描边~)

如下图

设置圆角、描边的Key为:

layer.borderWidth
layer.borderColorFromUIColor
layer.cornerRadius
clipsToBounds

我这次在测试时,

这样做不用关联出属性,但是需要输入大串字符串,也是不够方便。

高级

创建UIView的分类,使用IBInspectable+ IB_DESIGNABLE关键字:

#import <UIKit/UIKit.h>

IB_DESIGNABLE

@interface UIView (Inspectable)

@property(nonatomic,assign) IBInspectable CGFloat cornerRadius;
@property(nonatomic,assign) IBInspectable CGFloat borderWidth;
@property(nonatomic,assign) IBInspectable UIColor *borderColor;

@end
#import "UIView+Inspectable.h"

@implementation UIView (Inspectable)

-(void)setCornerRadius:(CGFloat)cornerRadius{
self.layer.masksToBounds = YES;
self.layer.cornerRadius = cornerRadius;
}
-(void)setBorderColor:(UIColor *)borderColor{
self.layer.borderColor = borderColor.CGColor;
}
-(void)setBorderWidth:(CGFloat)borderWidth{
self.layer.borderWidth = borderWidth;
}

- (CGFloat)cornerRadius{
return self.layer.cornerRadius;
}
- (CGFloat)borderWidth{
return self.layer.borderWidth;
}
- (UIColor *)borderColor{
return [UIColor colorWithCGColor:self.layer.borderColor];
}

@end

附上:GitHub地址

直接使用

直接将这两个文件拖入项目中即可使用,在右边栏将会显示圆角和描边的属性设置

如图:

动态显示设置效果

直接使用的话只有在运行时才能看到效果,

例如要实时显示一个UIBUtton圆角、描边效果,需要创建一个类继承UIButton

#import <UIKit/UIKit.h>
#import "UIView+Inspectable.h"

@interface myButton : UIButton

@end
#import "myButton.h"

@implementation myButton

@end

只要将button的Class选择该空白类即可

关于IBInspectableIB_DESIGNABLE的使用详情可以参考这篇文章《谈不完美的IBDesignable/IBInspectable可视化效果编程》

JSON转模型 For YYModel

· 阅读需 2 分钟
BY

JSON转模型是我们做iOS开发的基础技能,本文将通过YYModel这个框架安全快速的完成JSON到模型的转换,其中还会介绍到一款好用的插件ESJsonFormat

创建模型类我们可以通过ESJsonFormat这款插件快速完成。

使用方法:

将光标移动到代码行中 如下图的13行

然后点击Window->ESJsonFormat->Input JSON Window调出窗口

在窗口中输入你要解析的JSON文本,如下图:

Enter继续,然后神奇的一幕发生了

看到在.h中 所有的属性自动为你填上,而且帮你选好了类型

.m 也为你声明了list中成员的类型,不过这里需要稍作修改,因为我们需要用到YYModel进行解析,所以方法名改成modelContainerPropertyGenericClass

+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"list" : [List class]};
}

还有问题就是属性中出现关键字id,我们需要将id改为teacherId

然后在.m的implementation中声明,将字典的的id

+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"teacherId" : @"id"};
}

这样,模型的创建就完成了,剩下的就是用YYModel进行解析了

2、使用YYModel进行解析

解析很简单,就只需要一句话

// 将 JSON (NSData,NSString,NSDictionary) 转换为 Model:
Model *model = [Model yy_modelWithJSON:json];

// 或者
Model *model = [[Model alloc] init];
[model yy_modelSetWithDictionary:json];

到此,简便快速的完成了JSON到模型的转换。

最后,这里附上一篇YYModel的使用

Xcode Debug 大全

· 阅读需 7 分钟
BY

BUG,简单来说就是程序运行结果与预期的不同,下面来说说Xcode中的DEBUG方法

参考博文

断点调试

  • 普通断点
  • 全局断点
  • 条件断点

1.普通断点

看图

当程序运行到断点处时会停下,然后进行单步调试

2.全局断点

当程序运行出现崩溃时,就会自动断点到出现crash的代码行

3.条件断点

我们如果在一个循环里面使用了断点,如果这个循环执行了100万次,那你的断点要执行那么多次,你不觉得蛋蛋都凉了的忧伤么?所以我们这么做:

编辑断点

添加条件Condition

还可以Action中在条件断点触发时执行事件

如:输出信息

4.方法断点

打印调试(NSLog)

尽管ARC已经让内存管理变得简单、省时和高效,但是在object的life-cycles中跟踪一些重要事件依然十分重要。毕竟ARC并没有完全排除内存泄露的可能性,或者试图访问一个被release的对象。

  • NSLog

强化NSLog

//A better version of NSLog
#define NSLog(format, ...) do { \
fprintf(stderr, "<%s : %d> %s\n", \
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
__LINE__, __func__); \
(NSLog)((format), ##__VA_ARGS__); \
fprintf(stderr, "-------\n"); \
} while (0)

控制台输出

<ViewController.m : 32> -[ViewController viewDidLoad]
2016-10-14 17:33:31.022 DEUBG[12852:1238167] Hello World!
-------

利用NSString输出多种类型

  • 开启僵尸对象

Xcode可以把那些已经release掉得对象,变成“僵尸”,当我们访问一个Zombie对象时,Xcode可以告诉我们正在访问的对象是一个不应该存在的对象了。因为Xcode知道这个对象是什么,所以可以让我们知道这个对象在哪里,以及这是什么时候发生的。 所以Zombies是你的好基友!他可以让你输出的信息更具体!

具体这样做:(僵尸只能用在模拟器和OC语言)

控制台(lldb 命令)

LLDB 是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展。(这里有一个关于调试器如何工作的总体的解释。)

你以前有可能已经使用过调试器,即使只是在 Xcode 的界面上加一些断点。但是通过一些小的技巧,你就可以做一些非常酷的事情。GDB to LLDB 参考是一个非常好的调试器可用命令的总览。你也可以安装 Chisel,它是一个开源的 LLDB 插件合辑,这会使调试变得更加有趣。

参考:

与调试器共舞 - LLDB 的华尔兹

LLDB调试命令初探

About LLDB and Xcode

The LLDB Debugger

基础

help

在控制台输入help,显示控制台支持的lldb命令

print

打印值

缩写p

print是 expression -- 的缩写

printk可以指定格式打印 如 默认 p

十六进制 p/x

二进制 p/t

(lldb) p 16
16

(lldb) p/x 16
0x10

(lldb) p/t 16
0b00000000000000000000000000010000

(lldb) p/t (char)16
0b00010000

你也可以使用 p/c 打印字符,或者 p/s 打印以空终止的字符串 p/d打印ACRSII(译者注:以 '\0' 结尾的字符串)。

完整清单点击查看

po

打印对象,是 e -o --的缩写

expression

流程控制

当你通过 Xcode 的源码编辑器的侧边槽 (或者通过下面的方法) 插入一个断点,程序到达断点时会就会停止运行。

调试条上会出现四个你可以用来控制程序的执行流程的按钮。

从左到右,四个按钮分别是:continue,step over,step into,step out。

第一个,continue 按钮,会取消程序的暂停,允许程序正常执行 (要么一直执行下去,要么到达下一个断点)。在 LLDB 中,你可以使用 process continue 命令来达到同样的效果,它的别名为 continue,或者也可以缩写为 c。

第二个,step over 按钮,会以黑盒的方式执行一行代码。如果所在这行代码是一个函数调用,那么就不会跳进这个函数,而是会执行这个函数,然后继续。LLDB 则可以使用 thread step-over,next,或者 n 命令。

如果你确实想跳进一个函数调用来调试或者检查程序的执行情况,那就用第三个按钮,step in,或者在LLDB中使用 thread step in,step,或者 s 命令。注意,当前行不是函数调用时,next 和 step 效果是一样的。

大多数人知道 c,n 和 s,但是其实还有第四个按钮,step out。如果你曾经不小心跳进一个函数,但实际上你想跳过它,常见的反应是重复的运行 n 直到函数返回。其实这种情况,step out 按钮是你的救世主。它会继续执行到下一个返回语句 (直到一个堆栈帧结束) 然后再次停止。

frame info

会告诉你当前的行数和源码文件

(lldb) frame info
frame #0: 0x000000010a53bcd4 DebuggerDance`main + 68 at main.m:17

Thread Return

调试时,还有一个很棒的函数可以用来控制程序流程:thread return 。它有一个可选参数,在执行时它会把可选参数加载进返回寄存器里,然后立刻执行返回命令,跳出当前栈帧。这意味这函数剩余的部分不会被执行。这会给 ARC 的引用计数造成一些问题,或者会使函数内的清理部分失效。但是在函数的开头执行这个命令,是个非常好的隔离这个函数,伪造返回值的方式 。

(lldb) thread return NO

不用断点调试

在程序运行时,点击暂停按钮,即可进入调试状态,能对全局变量做操作

工具调试(instruments)

instruments Xcode自带许多工具供大家使用,打开方式如下图:

leaks内存泄漏检查工具

运行后查看

视图调试

启用视图调试:运行app过程中,按下底部的Debug View Hierarchy 按钮,或者从菜单中选择Debug > View Debugging > Capture View Hierarchy 来启动视图调试。

启动视图调试后,Xcode会对应用程序的视图层次拍一个快照并展示三维原型视图来探究用户界面的层级。该三维视图除了展示app的视图层次外,还展示每个视图的位置、顺序和视图尺寸,以及视图间的交互方式。

模拟器调试

编译并运行应用程序,选中模拟器,从 Debug菜单中选择Color Blended Layers选项。

然后会看到app的用户界面被红色和绿色覆盖,显示了哪些图层可以被叠加覆盖,以及哪些图层是透明的。混合层属于计算密集型视图,所以推荐尽可能地使用不透明的图层。

结语

目前所知道的调试方法大概就是上面这几种了,若有什么有趣的方法,请和我分享哈!