IOS开发工具大汇总(一)

1、crash分析工具

1.1  lldb+汇编分析: 最原生也是最强大的工具

1.2  Cycript: 参考文章:http://www.wildpointer.cn/ios-security-cycript-runtime-analysis/
1.3   



2、日志分析工具

2.1  mac机子上: 

               a、 phoneview               (mac版没有看到可以提取日志的地方,连root路径也没有)

                b、在xcode中徽标键+shift+2

               c、同步助手                       (mac版没有看到可以提取日志的地方,连root路径也没有)



日志分析过程

什么是崩溃日志,从哪里能得它?

iOS设备上的应用闪退时,操作系统会生成一个崩溃报告,也叫崩溃日志,保存在设备上。
崩溃日志上有很多有用的信息,包括应用是什么情况下闪退的。通常,上面有每个正在执行线程的完整堆栈跟踪信息,所以你能从中了解到闪退发生时各线程都在做什么,并分辨出闪退发生在哪个线程上。
有几种方法可以从设备上获取崩溃日志。
设备与电脑上的iTunes Store同步后,会将崩溃日志保存在电脑上。根据电脑操作系统的不同,崩溃日志将保存在以下位置:
Mac OS X:

~/Library/Logs/CrashReporter/MobileDevice/

Windows XP:

C:Documents and Settings<USERNAME>Application DataApple ComputerLogsCrashReporterMobileDevice<DEVICE_NAME>

Windows Vista or 7:

C:Users<USERNAME>AppDataRoamingApple ComputerLogsCrashReporterMobileDevice<DEVICE_NAME>

当用户抱怨闪退时,你可以要求他让设备与iTunes同步,并根据操作系统的不同,到上述位置把崩溃日志下载下来,然后通过电子邮件发送给你。
你必需尽量获取用户设备生成的所有崩溃日志。因为崩溃日志越多,就越容易诊断问题所在!
另外,如果你装了Xcode,也能很容易通过Xcode从你的设备上获得崩溃日志。将iOS设备连接到电脑上,然后打开Xcode。从菜单栏上选择 Window  菜单, 然后选择 Organizer (快捷方式是 Shift-CMD-2).
在 Organizer 窗口上, 选中 Devices 标签栏. 在左侧的导航面板上,选中 Device Logs, 如下图所示:
Devices tab in Xcode Organizer
看看上图,左侧有好几个 Device Logs 菜单项. LIBRARY 下面的Device Logs是你所有设备(曾经连接到Xcode的)的日志 。每个设备下面的 Device Logs 是对应设备的日志。
应用提交到App Store后,你也能从 iTunes Connect 获取到用户的崩溃日志. 登录到 iTunes Connect 上, 选择 Manage Your Applications, 点击相应的应用, 点击应用图标下面的 View Details 按钮, 然后点击右栏Links部分的  Crash Reports 。
Crash Reports in iTunes Connect
如果没有崩溃日志,试试点击Refresh 按钮刷新一下。如果你的应用还卖得不多,或者刚上架不久,iTunes Connect账号上也可能还没有任何崩溃日志。
如果iTunes Connect上有崩溃日志,你将看到如下图:
Crash Reports in iTunes Connect
有时,尽管有用户报告闪退,你仍然看不到崩溃报告。这时,最好让用户直接把崩溃报告发送给你。

什么情况下会产生崩溃日志?

两种主要情况会产生崩溃日志:

  1. 应用违反操作系统规则。
  2. 应用中有Bug。

违反iOS规则包括在启动、恢复、挂起、退出时watchdog超时、用户强制退出和低内存终止。让我们详细了解一下吧。
Watchdog 超时机制
从iOS 4.x开始,退出应用时,应用不会立即终止,而是退到后台。
但是,如果你的应用响应不够快,操作系统有可能会终止你的应用,并产生一个崩溃日志。这些事件与下列UIApplicationDelegate方法相对应:

  • application:didFinishLaunchingWithOptions:
  • applicationWillResignActive:
  • applicationDidEnterBackground:
  • applicationWillEnterForeground:
  • applicationDidBecomeActive:
  • applicationWillTerminate:

上面所有这些方法,应用只有有限的时间去完成处理。如果花费时间太长,操作系统将终止应用。

注意: 如果你没有把需要花费时间比较长的操作(如网络访问)放在后台线程上就很容易发生这种情况。关于如果避免这种情况的信息,请参考我们的另外两篇教程: Grand Central Dispatch 和 NSOperations

用户强制退出
iOS 4.x开始支持多任务。如果应用阻塞界面并停止响应, 用户可以通过在主屏幕上双击Home按钮来终止应用。此时,操作应用将生成一个崩溃日志。

注意: 双击Home按钮后,你将看到运行过的所有应用。那些应用不一定是正在运行,也不一定是被挂起。
通常,用户点击Home按钮时,应用将在后台保留约10分钟,然后操作系统自动将其终止。 所以双击Home按钮显示的应用列表只是表明那是一系列过去打开过的应用。删除那些应用的图标不会产生任何崩溃日志。

低内存终止
子类化UIViewController时,你或许已经注意到didReceiveMemoryWarning方法。
在前台运行的应用拥有访问和使用内存的最高优化级。然而,这并不意味着该应用能使用设备的所有可用内存 ——每个应用只能使用一部分可用内存。
当内存使用达到一定程度时,操作系统将发出一个 UIApplicationDidReceiveMemoryWarningNotification 通知。同时,调用 didReceiveMemoryWarning 方法。
此时,为了让应用继续正常运行,操作系统开始终止在后台的其他应用以释放一些内存。所有后台应用被终止后,如果你的应用还需要更多内存,操作系统会将你的应用也终止掉,并产生一个崩溃日志。而在这种情况下被终止的后台应用,不会产生崩溃日志。

注意: 根据 苹果文档, Xcode不会自动添加低内存日志。你必需手动获取日志。 然而,根据我的个人经验,使用 Xcode 4.5.2, 低内存日志也会自动导入,只是”Process”和”Type”属性都被标为Unknown(未知)。
另外,值得一提的是在极短时间内分配一大块内存将给系统内存带来巨大负担。这样,也会产生内存警告的通知。

应用中有Bug
如你所想,大多数闪退都是由于应用中有Bug,因此大多数崩溃日志的产生都是因为应用中的Bug。Bug的种类的有很多。

在本教程的后半部分,你将通过调试一个会产生崩溃日志的含有Bug的应用,学习如何找到问题所在并进行修复!

崩溃日志的实例

让我们看看一个崩溃日志的实例,以使你在处理一些实际问题之前心里有谱。
事不宜迟,见见你的新朋友吧:

// 1: 进程信息
Incident Identifier: 30E46451-53FD-4965-896A-457FC11AD05F
CrashReporter Key:   5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model:      iPhone4,1
Process:         Rage Masters [4155]
Path:            /var/mobile/Applications/A5635B22-F5EF-4CEB-94B6-FE158D885014/Rage Masters.app/Rage Masters
Identifier:      Rage Masters
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]
// 2: 基本信息
Date/Time:       2012-10-17 21:39:06.967 -0400
OS Version:      iOS 6.0 (10A403)
Report Version:  104
// 3: 异常
Exception Type:  00000020
Exception Codes: 0x000000008badf00d
Highlighted Thread:  0
// 4: 线程回溯
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libsystem_kernel.dylib        	0x327f2eb4 mach_msg_trap + 20
1   libsystem_kernel.dylib        	0x327f3048 mach_msg + 36
2   CoreFoundation                	0x36bd4040 __CFRunLoopServiceMachPort + 124
3   CoreFoundation                	0x36bd2d9e __CFRunLoopRun + 878
4   CoreFoundation                	0x36b45eb8 CFRunLoopRunSpecific + 352
5   CoreFoundation                	0x36b45d44 CFRunLoopRunInMode + 100
6   CFNetwork                     	0x32ac343e CFURLConnectionSendSynchronousRequest + 330
7   Foundation                    	0x346e69ba +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 242
8   Rage Masters                  	0x000d4046 0xd2000 + 8262
Thread 1:
0   libsystem_kernel.dylib        	0x32803d98 __workq_kernreturn + 8
1   libsystem_c.dylib             	0x3a987cf6 _pthread_workq_return + 14
2   libsystem_c.dylib             	0x3a987a12 _pthread_wqthread + 362
3   libsystem_c.dylib             	0x3a9878a0 start_wqthread + 4
// 5: 线程状态
Thread 0 crashed with ARM Thread State (32-bit):
    r0: 0x00000000    r1: 0x00000000      r2: 0x00000001      r3: 0x39529fc8
    r4: 0xffffffff    r5: 0x2fd7d301      r6: 0x2fd7d300      r7: 0x2fd7d9d0
    r8: 0x2fd7d330    r9: 0x3adbf8a8     r10: 0x2fd7d308     r11: 0x00000032
    ip: 0x00000025    sp: 0x2fd7d2ec      lr: 0x001bdb25      pc: 0x30301838
  cpsr: 0x00000010
// 6: 二进制映像
Binary Images:
0xd2000 -    0xd7fff +Rage Masters armv7   /var/mobile/Applications/A5635B22-F5EF-4CEB-94B6-FE158D885014/Rage Masters.app/Rage Masters
0x2fe41000 - 0x2fe61fff  dyld armv7   /usr/lib/dyld
0x327f2000 - 0x32808fff  libsystem_kernel.dylib armv7   /usr/lib/system/libsystem_kernel.dylib
0x328a8000 - 0x328bdfff  libresolv.9.dylib armv7   /usr/lib/libresolv.9.dylib
0x32a70000 - 0x32b35fff  CFNetwork armv7   /System/Library/Frameworks/CFNetwork.framework/CFNetwork
0x32b7a000 - 0x32cc3fff  libicucore.A.dylib armv7   /usr/lib/libicucore.A.dylib
0x32cc4000 - 0x32cc5fff  CoreSurface armv7   /System/Library/PrivateFrameworks/CoreSurface.framework/CoreSurface
0x32f65000 - 0x32f8afff  OpenCL armv7   /System/Library/PrivateFrameworks/OpenCL.framework/OpenCL

这报告看起来像天书。:) 我们分几部分来解读吧:
(1) 进程信息
第一部分是闪退进程的相关信息。

  • Incident Identifier是崩溃报告的唯一标识符。
  • CrashReporter Key 是与设备标识相对应的唯一键值。虽然它不是真正的设备标识符,但也是一个非常有用的情报:如果你看到100个崩溃日志的CrashReporter Key值都是相同的,或者只有少数几个不同的CrashReport值,说明这不是一个普遍的问题,只发生在一个或少数几个设备上。
  • Hardware Model 标识设备类型。 如果很多崩溃日志都是来自相同的设备类型,说明应用只在某特定类型的设备上有问题。上面的日志里,崩溃日志产生的设备是iPhone 4s。
  • Process 是应用名称。中括号里面的数字是闪退时应用的进程ID。
  • 接下来几行不言自明,无需赘述。

(2) 基本信息
这部分给出了一些基本信息,包括闪退发生的日期和时间,设备的iOS版本。如果有很多崩溃日志都来自iOS 6.0,说明问题只发生在iOS 6.0上。
(3) 异常
在这部分,你可以看到闪退发生时抛出的异常类型。还能看到异常编码和抛出异常的线程。根据崩溃报告类型的不同,在这部分你还能看到一些另外的信息。
(4) 线程回溯
这部分提供应用中所有线程的回溯日志。 回溯是闪退发生时所有活动帧清单。它包含闪退发生时调用函数的清单。看下面这行日志:

2    XYZLib    0x34648e88    0x83000 + 8740

它包括四列:

  1. 帧编号—— 此处是2。
  2. 二进制库的名称 ——此处是 XYZLib.
  3. 调用方法的地址 ——此处是 0x34648e88.
  4. 第四列分为两个子列,一个基本地址和一个偏移量。此处是0×83000 + 8740, 第一个数字指向文件,第二个数字指向文件中的代码行。

(5) 线程状态
这部分是闪退时寄存器中的值。一般不需要这部分的信息,因为回溯部分的信息已经足够让你找出问题所在。
(6) 二进制映像
这部分列出了闪退时已经加载的二进制文件。

符号化Symbolication

第一次看到崩溃日志上的回溯时,你或许会觉得它没什么意义。我们习惯使用方法名和行数,而非像这样的神秘位置:

6    Rage Masters    0x0001625c    0x2a000 + 3003

将这些十六进制地址转化成方法名称和行数的过程称之为符号化。
从Xcode的Organizer窗口获取崩溃日志后过几秒钟,崩溃日志将被自动符号化。上面那行被符号化后的版本如下 :

6    Rage Masters    0x0001625c    -[RMAppDelegate application:didFinishLaunchingWithOptions:] (RMAppDelegate.m:35)

Xcode符号化崩溃日志时,需要访问与App Store上对应的应用二进制文件以及生成二进制文件时产生的 .dSYM 文件。必需完全匹配才行。否则,日志将无法被完全符号化。
所以,保留每个分发给用户的编译版本非常重要。提交应用前进行归档时,Xcode将保存应用的二进制文件。可以在Xcode Organizer的Archives标签栏下找到所有已归档的应用文件。
在发现崩溃日志时,如果有相匹配的.dSYM文件和应用二进制文件,Xcode会自动对崩溃日志进行符号化。如果你换到别的电脑或创建新的账户,务必将所有二进制文件移动到正确的位置,使Xcode能找到它们。

注意: 你必需同时保留应用二进制文件和.dSYM文件才能将崩溃日志完整符号化。每次提交到iTunes Connect的构建都必需归档。
.dSYM文件和二进制文件是特定绑定于每一次构建和后续构建的,即使来自相同的源代码文件,每一次构建也与其他构建不同,不能相互替换。
如果你使用Build 和 Archive 命令,这些文件会自动放在适当位置。 如果不是使用Build 和 Archive命令,放在Spotlight能够搜索到的位置(比如Home目录)即可。

 

低内存闪退

因为低内存崩溃日志与普通崩溃日志略有不同,所以本教程特别分开说明一下。:]
iOS设备检测到低内存时,虚拟内存系统发出通知请求应用释放内存。这些通知发送到所有正在运行的应用和进程,试图收回一些内存。
如果内存使用依然居高不下,系统将会终止后台线程以缓解内存压力。如果可用内存足够,应用将能够继续运行而不会产生崩溃报告。否则,应用将被iOS终止,并产生低内存崩溃报告。
低内存崩溃日志上没有应用线程的堆栈回溯。相反,上面显示的是以内存页数为单位的各进程内存使用量。(在撰写本文的时候,一个内存页的大小是4KB。)
被iOS因释放内存页终止的进程名称后面你会看到jettisoned 字样。如果看到它出现在你的应用名称后面,说明你的应用因使用太多内存而被终止了。
低内存崩溃日志看起来像这样:

Incident Identifier: 30E46451-53FD-4965-896A-457FC11AD05F
CrashReporter Key:   5a56599d836c4f867f6eec76afee451bf9ae5f31
OS Version:          iPhone OS 3.1.3 (7E18)
Date/Time:           2012-10-17 21:39:06.967 -0400
Free pages:        96
Wired pages:       10558
Purgeable pages:   0
Largest process:   Rage Masters
Processes
         Name                 UUID                    Count resident pages
    Rage Masters     9320 (jettisoned) (active)
    mediaserverd      255
     dataaccessd      505
         syslogd       71
            apsd      171
       securityd      243
         notifyd     2027
      CommCenter      189
     SpringBoard     2158 (active)
      accessoryd       91
         configd      371
       fairplayd       93
   mDNSResponder      292
       lockdownd     1204
         launchd       72

当应用发生低内存闪退时,你必需看看应用中内存使用的方式,以及是如何处理低内存警告的。你可以使用Instruments工具中使用Allocations 和 Leaks来发现内存分配问题和内存泄漏问题。如果你不知道如何利用 Instruments 检查内存问题,可以看看这个教程 。
还有,别忘记虚拟内存! Instruments工具的Leaks 和 Allocations 不能跟踪显存使用情况。必需使用 VM Tracker 才能查看显存使用情况。
VM Tracker 默认是关闭的。打开Instrument,手动 选中Automatic Snapshotting 标志或者按下Snapshot Now 按钮。
本教程后面将会学习如何研究低内存崩溃日志。

异常编码

在研究真实闪退场景之前,还有一点需要重点介绍一下:就是那些有趣的异常编码 。
你可以在报告的异常部分——前面代码的第3部分找到异常编码。有些编码比较常见。
通常,异常编码以一些文字开头,紧接着是一个或多个十六进制值,此数值正是说明闪退根本性质的所在。  从这些编码中,可以区分出闪退是因为程序错误、非法内存访问或者是其他原因。
下面是一些常见的异常编码:

  • 0x8badf00d: 读做 “ate bad food”! (把数字换成字母,是不是很像 :p)该编码表示应用是因为发生watchdog超时而被iOS终止的。  通常是应用花费太多时间而无法启动、终止或响应用系统事件。

  • 0xbad22222: 该编码表示 VoIP 应用因为过于频繁重启而被终止。
  • 0xdead10cc: 读做 “dead lock”!该代码表明应用因为在后台运行时占用系统资源,如通讯录数据库不释放而被终止 。
  • 0xdeadfa11: 读做 “dead fall”! 该代码表示应用是被用户强制退出的。根据苹果文档, 强制退出发生在用户长按开关按钮直到出现 “滑动来关机”, 然后长按 Home按钮。强制退出将产生 包含0xdeadfa11 异常编码的崩溃日志, 因为大多数是强制退出是因为应用阻塞了界面。

 

注意: 在后台任务列表中关闭已挂起的应用不会产生崩溃日志。 一旦应用被挂起,它何时被终止都是合理的。所以不会产生崩溃日志。

大展身手的时候到了!

好了! 你已经学习了所有分析崩溃日志和修复错误的基础知识!
假设你刚进入Rage-O-Rage有限公司工作。该公司有一个在App Store上热销的应用,叫 Rage Masters。
你的老板安迪要你帮忙解决几个用户经常抱怨闪退问题。你的任务就是研究这些闪退,符号化用户提供的崩溃日志,查找问题所在,并修复之。
你可以从 这里下载应用的源代码。

注意: 如果你想自己重新生成崩溃报告,请遵照以下指引:

  1. 下载源码然后在Xcode中打开工程文件。
  2. 使用正确的provisioning profile连接到iOS设备。
  3. 从Xcode工具栏上选择iOS设备——不是模拟器作为target,然后构建应用。
  4. 当你在设备上到默认页面(应用的全屏图片)时,立即在Xcode上点击停止按钮。
  5. 关闭 Xcode。
  6. 在设备上直接打开应用。
  7. 测试场景,完成后连接设备到电脑上,通过Xcode获取崩溃日志。

 

场景 1: 糟糕的代码

一封来自用户的邮件: “大哥,你的应用就是一坨屎! 我将其下载到我自己的iPod Touch和iPhone上,还下载到我儿子的iPod Touch上。在所有的设备上,都是还没打开就闪退了……”
别一封来自用户的邮件说, “我下载了你们的应用,一打开就闪退。真悲催…”
另一封邮件说得更明确:”你们的应用不能运行。我把它下载到我和妻子的设备上。所有设备都是 一打开就闪退了…”

好吧,别灰心! 这些意见藏着什么玄机呢?让我们看看崩溃日志吧:

Incident Identifier: 85833DBA-3DF7-43EE-AF80-4E5C51091F42
CrashReporter Key:   5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model:      iPhone4,1
Process:         Rage Masters [20067]
Path:            /var/mobile/Applications/B2121A89-3D1F-4E61-BB18-5511E1DC150F/Rage Masters.app/Rage Masters
Identifier:      Rage Masters
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]
Date/Time:       2012-11-03 13:37:31.148 -0400
OS Version:      iOS 6.0 (10A403)
Report Version:  104
Exception Type:  00000020
Exception Codes: 0x000000008badf00d
Highlighted Thread:  0
Application Specific Information:
Soheil-Azarpour.Rage-Masters failed to launch in time
Elapsed total CPU time (seconds): 8.030 (user 8.030, system 0.000), 20% CPU 
Elapsed application CPU time (seconds): 3.840, 10% CPU
Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   libsystem_kernel.dylib        	0x327f2eb4 mach_msg_trap + 20
1   libsystem_kernel.dylib        	0x327f3048 mach_msg + 36
2   CoreFoundation                	0x36bd4040 __CFRunLoopServiceMachPort + 124
3   CoreFoundation                	0x36bd2d9e __CFRunLoopRun + 878
4   CoreFoundation                	0x36b45eb8 CFRunLoopRunSpecific + 352
5   CoreFoundation                	0x36b45d44 CFRunLoopRunInMode + 100
6   CFNetwork                     	0x32ac343e CFURLConnectionSendSynchronousRequest + 330
7   Foundation                    	0x346e69ba +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 242
8   Rage Masters                  	0x000ea1c4 -[RMAppDelegate application:didFinishLaunchingWithOptions:] (RMAppDelegate.m:36)
9   UIKit                         	0x37f30ad4 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 248
10  UIKit                         	0x37f3065e -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1186
11  UIKit                         	0x37f28846 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 694
12  UIKit                         	0x37ed0c3c -[UIApplication handleEvent:withNewEvent:] + 1000
13  UIKit                         	0x37ed06d0 -[UIApplication sendEvent:] + 68
14  UIKit                         	0x37ed011e _UIApplicationHandleEvent + 6150
15  GraphicsServices              	0x370835a0 _PurpleEventCallback + 588
16  GraphicsServices              	0x370831ce PurpleEventCallback + 30
17  CoreFoundation                	0x36bd4170 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32
18  CoreFoundation                	0x36bd4112 __CFRunLoopDoSource1 + 134
19  CoreFoundation                	0x36bd2f94 __CFRunLoopRun + 1380
20  CoreFoundation                	0x36b45eb8 CFRunLoopRunSpecific + 352
21  CoreFoundation                	0x36b45d44 CFRunLoopRunInMode + 100
22  UIKit                         	0x37f27480 -[UIApplication _run] + 664
23  UIKit                         	0x37f242fc UIApplicationMain + 1116
24  Rage Masters                  	0x000ea004 main (main.m:16)
25  libdyld.dylib                 	0x3b630b1c start + 0

发现问题了吗? 异常编码是0x000000008badf00d,还有后面的报告:

Application Specific Information:
Soheil-Azarpour.Rage-Masters failed to launch in time
Elapsed total CPU time (seconds): 8.030 (user 8.030, system 0.000), 20% CPU 
Elapsed application CPU time (seconds): 3.840, 10% CPU

这说明应用在启动时就闪退了,iOS的watchdog机制终止了应用。帅! 找到问题了,但是为什会发生这样的事呢?
接着往下看日志。 从下向上读回溯日志。最底下的帧 (frame 25: libdyld.dylib)是最先调用的,然后是帧24, Rage Masters, main (main.m:16) ,依此类推。
跟应用源代码相关的帧是最重要的。忽略掉系统库和框架。下一个与代码相关的帧是:

8    Rage Masters    0x0009f244 -[RMAppDelegate application:didFinishLaunchingWithOptions:] (RMAppDelegate.m:35)

应用在执行RMAppDelegate (RMAppDelegate.m:35)类application:didFinishLaunchingWithOptions: 方法第35 行代码时闪退。打开Xcode看看那行代码:

NSData *directoryData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

就是它了! 同步调用web服务?! 在主线程上?! 在 application:didFinishLaunchingWithOptions: 方法上?!! 谁写的代码呀?!

Network calls on the main thread makes kittens sad.

Network calls on the main thread makes kittens sad.

不管如何,问题得你来修复了。这个调用必需异步进行,甚至更理想的情况是,在application:didFinishLaunchingWithOptions:返回YES之后的其他部分再执行Web服务。
在其他地方调用可能需要比较多的修改。当下,我们只要使应用不闪退就行。可以在日后再实现更好的设计。 将上面那行讨厌的代码(及其下面的三行代码)换成下面这个异步的版本吧:

[NSURLConnection sendAsynchronousRequest:request
                                           queue:[NSOperationQueue mainQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
         {
             NSURL *cacheDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSUserDirectory inDomains:NSCachesDirectory] lastObject];
             NSURL *filePath = [NSURL URLWithString:kDirectoryFile relativeToURL:cacheDirectory];
             [data writeToFile:[filePath absoluteString] atomically:YES];
         }];

 

场景 2: 无法响应事件的按钮

一名用户说: “我不能将某个rage master添加到书签里面。我想添加的时候应用就闪退…”
用一名用户说 :”书签不能用 … 在详细页面上,点击书签按钮,应用就闪退了!”
上面的抱怨说得不是很清楚,引起问题的原因肯定有多样。看看崩溃日志:

Incident Identifier: 3AAA63CC-3088-41CC-84D9-82FE03F9F354
CrashReporter Key:   5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model:      iPhone4,1
Process:         Rage Masters [20090]
Path:            /var/mobile/Applications/B2121A89-3D1F-4E61-BB18-5511E1DC150F/Rage Masters.app/Rage Masters
Identifier:      Rage Masters
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]
Date/Time:       2012-11-03 13:39:00.081 -0400
OS Version:      iOS 6.0 (10A403)
Report Version:  104
Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Crashed Thread:  0
Last Exception Backtrace:
0   CoreFoundation                	0x36bff29e __exceptionPreprocess + 158
1   libobjc.A.dylib               	0x34f0f97a objc_exception_throw + 26
2   CoreFoundation                	0x36c02e02 -[NSObject(NSObject) doesNotRecognizeSelector:] + 166
3   CoreFoundation                	0x36c0152c ___forwarding___ + 388
4   CoreFoundation                	0x36b58f64 _CF_forwarding_prep_0 + 20
5   UIKit                         	0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68
6   UIKit                         	0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26
7   UIKit                         	0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40
8   UIKit                         	0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498
9   UIKit                         	0x37fbade4 -[UIControl touchesEnded:withEvent:] + 484
10  UIKit                         	0x37ee35f4 -[UIWindow _sendTouchesForEvent:] + 520
11  UIKit                         	0x37ed0804 -[UIApplication sendEvent:] + 376
12  UIKit                         	0x37ed011e _UIApplicationHandleEvent + 6150
13  GraphicsServices              	0x3708359e _PurpleEventCallback + 586
14  GraphicsServices              	0x370831ce PurpleEventCallback + 30
15  CoreFoundation                	0x36bd416e __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 30
16  CoreFoundation                	0x36bd4112 __CFRunLoopDoSource1 + 134
17  CoreFoundation                	0x36bd2f94 __CFRunLoopRun + 1380
18  CoreFoundation                	0x36b45eb8 CFRunLoopRunSpecific + 352
19  CoreFoundation                	0x36b45d44 CFRunLoopRunInMode + 100
20  GraphicsServices              	0x370822e6 GSEventRunModal + 70
21  UIKit                         	0x37f242fc UIApplicationMain + 1116
22  Rage Masters                  	0x000ca004 main (main.m:16)
23  libdyld.dylib                 	0x3b630b1c start + 0

异常代码是SIGABRT。通常,  SIGABRT 异常是由于某个对象接收到未实现的消息引起的。 或者,用简单的话说,在某个对象上调用了不存在的方法。
这种情况一般不会发生,因为A对象调用了B方法,如果B方法不存在,编译器会报错。但是,如果你是使用selector间接调用方法的,编译器则无法检测对象是否存在该方法了。
回到崩溃日志。它指出闪退发生在编号为0的线程上。 这意味着很可能是在主线程上调用了某个对象没有实现的方法。
如果你接着阅读回溯日志,会发现跟你的代码相关的只有帧22, main.m:16. 这没有多大帮助。 :[
继续向上查看框架调用,出现这个:

2    CoreFoundation    0x36c02e02 -[NSObject(NSObject) doesNotRecognizeSelector:] + 166

这不是你自己写的代码。但至少它确认了是对象调用了一个没有实现的方法。
回到RMDetailViewController.m文件, 因为那是书签按钮实现动作的地方。 找到书签功能代码:

-(IBAction)bookmarkButtonPressed {
 
    self.master.isBookmarked = !self.master.isBookmarked;
 
    // Update shared bookmarks
    if (self.master.isBookmarked)
        [[RMBookmarks sharedBookmarks] bookmarkMaster:self.master];
    else
        [[RMBookmarks sharedBookmarks] unbookmarkMaster:self.master];
 
    // Update UI
    [self updateBookmarkImage];
}

看起来没什么问题,再检查一下storyboard (XIB文件) ,确认按钮连接的正确性。

就是它了! 在 MainStoryboard.storyboard,按钮连接的是 bookmarkButtonPressed: 而不是bookmarkButtonPressed (注意后面的分号说明方法有一个参数)。 只要将上面的方法签名修改成这样就能修复问题了:

-(IBAction)bookmarkButtonPressed:(id)sender {
    // Remain unchanged..
}

当然,你也可以简单地在XIB文件上删除错误的连接,然后重新连接方法,使XIB文件连接到正确的方法上。两者方法都行。
又处理了一个闪退问题,好样的。:]

场景 3: 表格上的Bug

另一用户抱怨道, “在书签视图上无法删除书签…” 还有另一用户抱怨同样的问题, “当我试图删除书签时,应用闪退…”
这些邮件没什么作用,还是看看崩溃日志!

Incident Identifier: 5B62D681-D8FE-41FE-8D52-AB7E6D6B2AC7
CrashReporter Key:   5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model:      iPhone4,1
Process:         Rage Masters [20088]
Path:            /var/mobile/Applications/B2121A89-3D1F-4E61-BB18-5511E1DC150F/Rage Masters.app/Rage Masters
Identifier:      Rage Masters
Version:         ??? (???)
Code Type:       ARM (Native)
Parent Process:  launchd [1]
Date/Time:       2012-11-03 13:38:45.762 -0400
OS Version:      iOS 6.0 (10A403)
Report Version:  104
Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Crashed Thread:  0
Last Exception Backtrace:
0   CoreFoundation                	0x36bff29e __exceptionPreprocess + 158
1   libobjc.A.dylib               	0x34f0f97a objc_exception_throw + 26
2   CoreFoundation                	0x36bff158 +[NSException raise:format:arguments:] + 96
3   Foundation                    	0x346812aa -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 86
4   UIKit                         	0x37f04b7e -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] + 7690
5   UIKit                         	0x3803a4a2 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] + 22
6   Rage Masters                  	0x000fd9ca -[RMBookmarksViewController tableView:commitEditingStyle:forRowAtIndexPath:] (RMBookmarksViewController.m:68)
7   UIKit                         	0x3809a5d4 -[UITableView(UITableViewInternal) animateDeletionOfRowWithCell:] + 80
8   UIKit                         	0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68
9   UIKit                         	0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26
10  UIKit                         	0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40
11  UIKit                         	0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498
12  UIKit                         	0x37fbb0a8 -[UIApplication sendAction:to:from:forEvent:] + 68
13  UIKit                         	0x37fbb05a -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26
14  UIKit                         	0x37fbb038 -[UIControl sendAction:to:forEvent:] + 40
15  UIKit                         	0x37fba8ee -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498
16  UIKit                         	0x37fbade4 -[UIControl touchesEnded:withEvent:] + 484
17  UIKit                         	0x37ee35f4 -[UIWindow _sendTouchesForEvent:] + 520
18  UIKit                         	0x37ed0804 -[UIApplication sendEvent:] + 376
19  UIKit                         	0x37ed011e _UIApplicationHandleEvent + 6150
20  GraphicsServices              	0x3708359e _PurpleEventCallback + 586
21  GraphicsServices              	0x370831ce PurpleEventCallback + 30
22  CoreFoundation                	0x36bd416e __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 30
23  CoreFoundation                	0x36bd4112 __CFRunLoopDoSource1 + 134
24  CoreFoundation                	0x36bd2f94 __CFRunLoopRun + 1380
25  CoreFoundation                	0x36b45eb8 CFRunLoopRunSpecific + 352
26  CoreFoundation                	0x36b45d44 CFRunLoopRunInMode + 100
27  GraphicsServices              	0x370822e6 GSEventRunModal + 70
28  UIKit                         	0x37f242fc UIApplicationMain + 1116
29  Rage Masters                  	0x000fb004 main (main.m:16)
30  libdyld.dylib                 	0x3b630b1c start + 0

这看起来跟前面那个崩溃日志很像。是另一个SIGABRT 异常。 你可能想知道是否是相同的问题:发送信息到一个没有实现相应方法的对象?
让我们从回溯日志看看哪些方法被调用了。从底部开始,你的源代码最后被调用的是帧 6:

6    Rage Masters    0x00088c66 -[RMBookmarksViewController tableView:commitEditingStyle:forRowAtIndexPath:] (RMBookmarksViewController.m:68)

这是UITableViewDataSource 的一个方法. 呵呵?! 毫无疑问苹果已经实现了该方法 —— 你可以重载它, 但不像是还没有实现。而且,这是个可选的委派方法。 所以问题不是调用了一个没有实现的方法。
再看看上面的几个帧:

3    Foundation    0x346812aa -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 86
4    UIKit         0x37f04b7e -[UITableView(_UITableViewPrivate) _endCellAnimationsWithContext:] + 7690
5    UIKit         0x3803a4a2 -[UITableView deleteRowsAtIndexPaths:withRowAnimation:] + 22

帧 5, UITableView调用了它自己的另一个方法 deleteRowsAtIndexPaths:withRowAnimation: 然后是看起来像苹果内部方法的_endCellAnimationsWithContext: 被调用。然后Foundation framework发生异常handleFailureInMethod:object:file:lineNumber:description:.
这些分析结合用户的抱怨,看起来是你在处理UITableView删除行过程中有Bug。回到Xcode。你知道看哪里吗 ? 能从崩溃日志中判断出来? 就是RMBookmarksViewController.m文件的第68行:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
 
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

发现问题了吗? 给你点时间,仔细看一下。
找到了吧! 数据源呢? 代码在表格视图上删除了一行,但并没有修改背后的数据源。把上面的代码替换成下面的就能修复问题了:

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
 
    RMMaster *masterToDelete = [bookmarks objectAtIndex:indexPath.row];
    [bookmarks removeObject:masterToDelete];
    [[RMBookmarks sharedBookmarks] unbookmarkMaster:masterToDelete];
 
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

搞定了!走起,讨厌的 bug!!




3、内存泄露检查工具

3.1Clang Static Ananlyzer

这些工具和技术在技术文章 TN2239, iOS Debugging Magic 中描述。更确切地说,是使用NSZombie 来发现 release 过多的对象。 
你还可以使用 Instruments 来跟踪引用计数事件,并寻找内存泄露。参看 Viewing and Analyzing Trace Data. 


4、 iphone真机抓包工具

iFunbox v2.7



5、系统级查找问题工具


承了 UNIX 的优良传统,都是命令行工具,而且输出结果很容易被 grep(1)处理,因此这些工
具不仅适合手工使用,还适合在脚本中调用。 
5.1  filemon
第 3 章介绍了一个名为“filemon”的工具,这个工具可以实时显示 OS X 和 iOS 上文件
系统的活动。这个工具的命名是为了向 Russinovich 的同名工具致敬。这个简单的工具基于
OS X 和 iOS 5 上的 FSEvents 设备,能够跟踪文件系统相关的事件,例如文件的创建和删除。 
5.2  psx 
第 4 章介绍了一个名为 psx 的工具,这是一个类似 ps 的增强版工具,可以显示 OS X 中
进程和线程的相关信息。这个工具在第 4 章中特别重要,该章讲解了进程的内部结构,并且
演示了一个没有文档的系统调用 proc_info。如果查看的是自己创建的线程,这个工具不需要
有特别的权限,但是查看其他进程则需要 root 权限。这个工具可以从本书的支持网站免费下
载,带有完整的源代码。 
5.3   jtool 
对于大部分和二进制文件相关的功能,可以使用 OS X 自带的 otool(1),这个工具能够很
全面地分析数据区域(data section),而且由于 ARM 架构具有两种模式的汇编,所以显示 ARM
二进制文件的时候会让人感到混淆。jtool 的目标是增强 otool,不仅解决了 otool 的这些缺陷,
还提供了静态二进制分析的新特性。这个工具对第 4 章特别有用,该章详细分析了 Mach-O
文件格式,而且由于这个工具有很多有用的特性,例如寻找文件中的引用以及一些有限的反
汇编能力,所以在后面几章也挺有用。这个工具可以从本书的支持网站免费下载,但是是闭
源的。 
5.4   dEFI 
这个简单的程序可以导出 Intel Mac 上的固件(EFI)变量,还可以显示注册的 EFI提供商。
这个工具演示了基本的 EFI 编程技术——如何与引导服务和运行时服务进行接口。这个工具
可以免费下载,带有完整的源代码。第 6 章介绍这个工具。 深入解析 Mac OS X & iOS 操作系统 
 XII
5.5  joker 
第 8 章介绍的 joker 工具是一个简单的工具,这个工具可以用来和内核交互(特别是在 iOS
上)。这个工具可以查找并显示 iOS 和 OS X内核的系统调用和 Mach 陷阱表,显示 sysctl 结
构,并且在二进制文件中查找特定的模式。这个工具非常适合于逆向工程和黑客一类的活动,
因为 Apple 已经不再导出陷阱和系统调用符号了。 
5.6 corerupt 

本书提供了 corerupt 工具。通过这个工具可以将任何进程的虚拟内存映射以核心转储兼容的
格式导出到一个文件,类似于 Windows 的 Create Dump File 功能,也很像 Mac OS X Internals: 
A Systems Approach 一书中使用的 gcore 工具。corerupt 在其前身的基础上有了增强,提供了
对 ARM 的支持,还支持对虚拟内存映射进行入侵式的操作,例如修改内存页面。 
5.7   HFSleuth 
HFSleuth 是本书中的一个重要工具,这是一个用于查看 OS X 原生的文件系统 HFS+支撑
结构的多合一命令行工具。之所以开发这个工具,是因为确实没有什么其他方式能够展示这
个非常复杂的文件系统的内部工作原理。Singh 的书 Mac OS X Internals: A Systems Approach 
中也包含了一个类似的工具,称为 hfsdebug,这个工具的功能少一些,而且只支持 PowerPC,
后来被一个商业工具 fileXRay 所替代。 
为了在真实的文件系统上使用 HFSleuth,必须能够读这个文件系统。一种方法就是引导
这个文件系统。HFSleuth 的功能几乎都是只读的,所以可以保证这个工具非常安全。但是访
问文件系统所在的底层块设备(有时候是字符设备)的访问权限通常是 rw-r-----,意味着设备不
能被其他用户读访问。如果您通常情况下不信任 root,并且坚持要使用较低的权限(这是一个
明智的决定!),那么一个同等有效的替代方案是通过 chmod(1)修改 HFS+分区所在设备的权
限,使得自己的用户有权限读(通常使用 o+r 参数)。一些高级功能(例如修复和 HFS+/HFSX
转换)要求写访问权限。HFSleuth 可以从本书的支持网站免费下载,而且会一直免费。不过和
其前任一样,这个工具也不是开源的。 
5.8   lsock 
netstat -o 是一个大家都非常需要的功能,能够显示进程对系统中 socket 的所有权,但是
这个功能在 OS X 中就没有了。在 OS X 中,这个功能要通过 lsof(1)实现,但是后者需要剔除
其他打开的文件将 socket 滤出来,所以比较麻烦。另一个缺失的功能是显示正在创建的 socket
连接,就像 Windows 的 TCPMon 提供的功能一样。第 17 章介绍的这个工具使用了一个没有
正式文档的内核控制协议 com.apple.network.statistics,这个协议可以获得 socket 创建时候的
实时通知。这个工具特别容易集成到脚本中,因此可以很方便地用作连接事件处理程序。 
5.9   jkextstat 
使用的最后一个工具是 jkextstat,这是一个 kextstat(8)兼容的工具,能够列出内核
扩展。和最初的版本不一样,这个工具支持详细输出(verbose)模式,而且可以用于 iOS。因
此,这个工具对于 iOS 内核的探讨来说价值重大,而这件事在这本书出现之前是很困难的,
因为 iOS 的二进制 kextstat 使用了不再被支持的 API。这个工具在最初的版本上有了改进,
允许更详细的输出信息,能关注于特定的内核扩展,而且还支持输出到 XML格式。

插件和工具介绍内容均收集于网络,太多了就不一一注明了,在此谢过!

5.10、Charles

为了调试与服务器端的网络通讯协议,常常需要截取网络封包来分析。Charles通过将自己设置成系统的网络访问代理服务器,使得所有的网络访问请求都通过它来完成,从而实现了网络封包的截取和分析。一个可查看所有HTTP和SSL/HTTPS流量的工具。这款工具对于你测试和服务器端进行交互的应用非常有用

5.11、xScope

xScope带有六种不同的工具,帮助每一个设计者快速、精确的完成工作,这些工具功能灵活、强大,包括∶量度距离和角度的标尺、不同大小画面的屏幕、放大镜、精确垂直和水平标记辅助线、精确阔度, 高度和原来排成直线标记框架、找寻画面上全符合位置的 交叉点。

5.12、 Flurry

1.免费。可以收集用户的分布信息,用户对不同功能的使用频率,用户手机上的具体功能的执行信息(比如某按钮响应的执行时间)。并提供一些分析并以图表的方式显示出来,比如执行时间分布区间直方图,平均执行时间等等。

2.需要在网页上查看数据分析,刷新时间很慢。比如在你的程序执行后大约2个小时以上才能在网页上显示出来(GoogleAnalytics更是要24h,Localytics快一些二十分钟)。分析项目是固定的。图表显示不能更改或者自定义。不能直接看到所有数据(只能通过图表看到分析过了的数据分布的大概信息)

3.如果一定要获取所有的具体数据信息,可以通过Flurry的API来通过输入指定的网址和参数来获取指定的数据(XML或JSON

5.13、Crashlytics,

Crashlytics是基于云的错误报告解决方案,旨在找到所有崩溃应用的共同点。

通过写一行简单的代码来激活一个大概75KB的SDK,然后在AmazonWeb Services的云上来运行错误报告,最后给出关于你的应用崩溃的一个简单的报告。这是目前对移动开发者最有用最简单的错误报告系统。

5.14、Base

一款Mac OS X app,可用来创建、设计、编辑以及浏览SQLite 3数据库文件。

5.15、Pony Debugger

PonyDebugger是一个很给力的iOS调试工具,它的监视器安装在Chrome浏览器下做为插件使用,通过监视器和PonyDebugger的iOSSDK相辅相成,可以很好的监视App的运作情况.它的突出的亮点功能如下:

1:实时的检测应用与网络的交互情况

2:查看应用内CoreData的数据变化

3:实时反馈UI层的层级情况

5.16、Cocoapods

CocoaPods是一个可以帮你集中管理第三方库的开源项目,运行在Ruby的环境下,基于GitHub托管优势,你可以很方便的查阅目前依赖于CocoaPods的资源.

并且,CocoaPods是可靠,稳定,安全的.可以为你在使用第三方库资源时节省大部分的配置和部署时间.更专心的专注于Coding!

5.17、Appledoc

做项目的人多了,就需要文档了。今天开始尝试写一些项目文档。appledoc是一个可以帮你生成Objective-C代码注释的辅助工具,appledoc所生成的注释API文档与苹果类库的API文档保持一致.

这可以让Xcode能够识别出我们自己的API文档.

5.18、Dash

一款api查询,必备。

5.19、DashXcodeplugin:一个添加了Dash支持的Xcode插件。当使用Option-Click(或者作用相同的键盘快捷键)查看选中符号的文档时,该工具允许你使用Dash代替Xcode自己的文档查看器。

5.20、Simpholders

SimPholders可让你快速直接地访问iPhone模拟器应用的app文档。你可以通过SimPholders找到数据库文件、永久存储以及缓存。说白了,就是你不用去找app文件夹了,直接点击界面,他就会把那个文件夹直接给你打开!

5.21、PaintCode

PaintCode是一款简单的矢量图形绘制工具,通过PaintCode,你可以直接在画布上画图,并且能够直接生成适用于iOS的Objective-C代码。说白了,就是你画一条线,下边直接给出你代码!

5.22、Liya

可视化操作Sqlite以及CoreData!

5.23、Briefs

设计师可以利用Briefs设计完整的iPhone、iPad应用交互模型,并利用模拟器即时体验设计的成果

5.24、Omnigraffle

非常快地制作你的iPhone应用的演示界面。

5.25、GlyphishIcons

你可能能从上面的这些图标中看到Flipboard 和 Twitter 在iOS上的应用使用了其中的一些图标。是的,这些个小图标对你的开发很有帮助。

5.26、Git

分布式版本控制系统和源码管理系统,其优点是:快和简单易用。

5.27、XcodeColors:为应用调试输出添加有颜色的代码,从而简化调试,可与CocoaLumberjack一同使用。

5.28、ColorSense:一款Xcode插件,能让开发者更简单更视觉换地使用UIColor和NSColor。当光标位于UIColor实例上时,颜色将会出现在屏幕上。点击颜色则可打开颜色选择器。该插件的编辑菜单增加了一些项目来插入颜色或者暂时禁用颜色高亮。菜单没有默认的键盘快捷键,但你可以通过系统的键盘参数选择进行设置。

20、CocoaPodsXcodePlugin:一款Xcode插件,允许你直接从Xcode管理CocoaPod依赖。

21、XcodeSnippets:这视乎是最明显的一个,但却可以减少大量开发时间。创建起来像drag-and-drop那么简单,这里有很多优秀的示例(XcodeCodeSnippets、CodeSnippets )。

单元测试

22、Kiwi:适用于iOS的TDD/BDD测试平台。Kiwi让单元测试更加易读,类似Ruby测试工具RSpec。(相关阅读:TDD的iOS开发初步以及Kiwi使用入门 )

23、Specta:一个轻量级的Objective-C和Cocoa的TDD/BDD框架,类似Kiwi,有类似RSpec的语法。通常会结合Expecta框架一起使用。

24、Cedar:Kiwi和Specta都基于该框架,同样使用RSpec风格的语言。

上述Kiwi、Specta、Expecta以及Cedar都可以通过CocoaPods添加到你的项目中。

持续集成

25、xctool:xctool是来自Facebook的优秀开发工具,可以让你通过命令行创建和测试你的应用。除了比苹果提供的xcodebuild工具好用外,它的输出可以注入CI软件,因此更具灵活性。

26、Jenkins:一个开源的持续集成服务器,配置简单。通过Xcode插件,你可以用Jenkins来测试、签名、创建以及分发应用。Jenkins非常人性化,ANSI彩色输出,这些都是额外的功能。

27、Xcodebots:该工具自动化了创建、分析以及测试应用的过程。使用了Mac OSXserver和苹果的指令。

28、HockeyApp ($) :类似TestFlight的服务,但也包含崩溃报告和用户反馈。

29、TestFlight:知名的iOS应用测试平台。2014年3月,苹果收购了该平台的开发商Burstly。TestFlight表示将会停止开发 Android 产品,并且目前的beta 测试 SDK 将不支持新顾客注册。目前的顾客仍然可以使用 TestFlight。

30、Mou:OS X上一款Markdown的编辑器。非常适用于编写自述文件、变更日志以及其他方面的内容。

通用工具

31、HomeBrew:OS X上非常出色的包管理工具。

32、Alcatraz:开源的Xcode5包管理器,可以让你发现和安装插件、模板以及配色方案,无需手动复制文件。现已支持Xcode 5。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值