http://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs

This is a blog post by Soheil Moayedi Azarpour, an independent iOS developer. You can also find him on Google+.

Have you ever had the following experience as an app developer?

Before you submit your app, you perform a lot of testing to make sure your app runs flawlessly. It works fine on your device, but after the app is in the App Store, some users report crashes!

If you’re anything like me, you want your app to be A+. So you go back to your code to fix the crashes… but where do you look?

This is when iOS crash logs come in handy. In most cases, you’ll get very detailed and useful information about the cause of the crash, like feedback from a good teacher.

In this tutorial, you’ll learn about some common crash log scenarios, as well as how to acquire crash logs from development devices and iTunes Connect. You will learn about symbolication, and tracing back from log to code. You will also debug an application that can crash in certain situations.

OK, let’s get crashing!

What Is A Crash Log, and Where Do I Get One?

When an application crashes on an iOS device, the operating system creates a crash report or a crash log. The report is stored on the device.

You can find a lot of useful information in a crash log, including the conditions under which the application terminated. Usually, there is a complete stack trace of each executing thread, so you can see what was happening in every thread at the time of the crash, and identify the thread where the crash occurred.

There are many ways to get a crash log from a device.

A device that is synced with iTunes stores its crash logs on the machine. Depending on the OS, here are the locations where you can find them:

Mac OS X:

~/Library/Logs/CrashReporter/MobileDevice/<DEVICE_NAME>

Windows XP:

C:\Documents and Settings\<USERNAME>\Application Data\Apple Computer\Logs\CrashReporter\MobileDevice\<DEVICE_NAME>

Windows Vista or 7:

C:\Users\<USERNAME>\AppData\Roaming\Apple Computer\Logs\CrashReporter\MobileDevice\<DEVICE_NAME>

When your users experience a crash, you can instruct them to sync their device with iTunes, go to one of the above locations (depending on their OS), download the crash logs and email them to you.

You want any crash logs your users generate. The more crash logs you have, the better equipped you are to find and diagnose weaknesses in your app!

Also, you can easily get the crash reports for your devices via Xcode, if you have it installed. To do this, connect the iOS device to your computer and open Xcode. From the menu bar, select Window, then Organizer (the shortcut is Shift-CMD-2).

In the Organizer window, go to the Devices tab. On the left-hand side navigation pane, look for Device Logs, as shown in the image below:

As you can see in the screenshot, there are multiple Device Logs items. The Device Logs under LIBRARY contain all the logs from all devices you own (or have connected to Xcode). The Device Logs entries under each device are for that particular device.

Once your app is submitted, you can also get crash logs from your users using iTunes Connect. To do this, simply sign in to your iTunes Connect account, navigate to Manage Your Applications, click on the app you want crash logs for, click on the View Details button below the icon, and click on Crash Reports in the right-hand side pane in the Links section.

If there are no crash logs, try clicking on the Refresh button. If you haven’t sold many copies of your app, or if it has been available only for a short time, chances are that you don’t have any crash logs in your iTunes Connect account.

If you did have any crash logs in iTunes Connect, you would see something like this:

Sometimes you won’t see anything in here despite your users reporting a crash. In that case, it’s best to ask them to send you the crash logs if possible.

What Generates a Crash Log?

There are two main situations that can result in a crash log:

  1. Your app violates OS policies.
  2. There are bugs in your app.

iOS policy violations include things such as watchdog timeout at the time of launch, resume, suspend and quit; user force-quit; and low memory termination. Let’s go over these in more detail.

Watchdog Timeout

As you’re probably aware, since iOS 4.x, most of the time when you quit an iOS app, the app isn’t terminated – instead, it’s sent to the background.

However, there are times when the OS will terminate your app and generate a crash log if the app didn’t respond fast enough. These events correspond with the implementation of the following UIApplicationDelegate methods:

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

In all of the above methods, the app gets a limited amount of time to finish its processing. If the app takes too long, the OS will terminate the app.

Note: This can easily happen to you if you aren’t performing long running operations (like network access) on background threads. For information on how to avoid this, check out our Grand Central Dispatch andNSOperations tutorials.

User Force-Quit

iOS 4.x supports multitasking. If an app blocks the UI and stops responding, the user can double-tap the Home button from the Home screen and terminate the app. In this case, the OS generates a crash log.

Note: You may have noticed that when you double-tap the Home button, you also get a list of all the applications you’ve run in the past. Those apps are not necessarily running, nor are they necessarily suspended.

Usually an app gets about 10 minutes to stay in the background once the user hits the Home button, and then it gets terminated automatically by the OS. So the list of apps that you see by double-tapping the Home button is only a list of past app runs. Deleting the icons for those apps does not generate any crash logs.

Low Memory Termination

When subclassing UIViewController, you may have noticed the didReceiveMemoryWarning method.

Any app that is running in the foreground has the highest priority in terms of accessing and using memory. However, that does not mean the app gets all the available memory on the device – each app gets a portion of the available memory.

When total memory consumption hits a certain level, the OS sends out aUIApplicationDidReceiveMemoryWarningNotification notification. At the same time,didReceiveMemoryWarning is invoked for the app.

At this point, so that your app continues to run properly, the OS begins terminating apps in the background to free some memory. Once all background apps are terminated, if your app still needs more memory, the OS terminates your app and generates a crash log.

The OS does not generate a crash log for the background apps that were terminated under this circumstance.

Note: Per Apple documentation, Xcode does not add low memory logs automatically. You have to obtain them manually, as described above. However, in my personal experience using Xcode 4.5.2, low memory crash logs were imported automatically with “Process” and “Type” listed as unknown.

It is also worth mentioning that allocating a big chunk of memory in a very short period of time puts a strain on the system memory — even if for a fraction of a second. This, too, can result in memory warning notifications.

Bugs in the Application

As you can imagine, these cause the majority of app crashes, and hence, are behind the generation of most crash logs. Bugs come in seemingly endless varieties.

Later on in this tutorial, you will confront some actual crash logs from a buggy application, and use your powers of deduction to find the culprits and apply some fixes!

A Sample Crash Log

Let’s start by taking a look at a sample crash log, so that you have an idea of what to expect before tackling some real scenarios.

Without further ado, meet your new friend:

// 1: Process Information
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: Basic Information
Date/Time: 2012-10-17 21:39:06.967 -0400
OS Version: iOS 6.0 (10A403)
Report Version: 104
 
// 3: Exception
Exception Type: 00000020
Exception Codes: 0x000000008badf00d
Highlighted Thread: 0
 
// 4: Threads backtraces
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 state
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
Binary Images:
0xd2000 - 0xd7fff +Rage Masters armv7 <f37ee6d2c7b334868972e0e9c54f7062> /var/mobile/Applications/A5635B22-F5EF-4CEB-94B6-FE158D885014/Rage Masters.app/Rage Masters
0x2fe41000 - 0x2fe61fff dyld armv7 <75594988728831d98e1f7c4c7b7ca29d> /usr/lib/dyld
0x327f2000 - 0x32808fff libsystem_kernel.dylib armv7 <f167dacec44b3a86a8eee73400ff7a83> /usr/lib/system/libsystem_kernel.dylib
0x328a8000 - 0x328bdfff libresolv.9.dylib armv7 <e79b59a3406f34d9b37f8085955115ce> /usr/lib/libresolv.9.dylib
0x32a70000 - 0x32b35fff CFNetwork armv7 <3e973794a4d13428bb974edcb2027139> /System/Library/Frameworks/CFNetwork.framework/CFNetwork
0x32b7a000 - 0x32cc3fff libicucore.A.dylib armv7 <0253932c1b9038a0849ef73c38e076ca> /usr/lib/libicucore.A.dylib
0x32cc4000 - 0x32cc5fff CoreSurface armv7 <b3f9d4e8dd803a48b88c58a0663d92a3> /System/Library/PrivateFrameworks/CoreSurface.framework/CoreSurface
0x32f65000 - 0x32f8afff OpenCL armv7 <f7706501012430fc94ed99006419fba9> /System/Library/PrivateFrameworks/OpenCL.framework/OpenCL

There is a lot of mysterious stuff in this report. :] Let’s go through it section-by-section:

(1) Process Information

This first section gives you some information about the process that crashed.

  • Incident Identifier is a unique identifier for the crash report.
  • CrashReporter Key is also a unique key that is mapped to the device identifier. Although it is anonymized, it gives you a very useful piece of information: if you see 100 crash logs from the same CrashReporter Key or a few CrashReporter Keys, it means that the issue might not be a widespread problem, limited to only one or a few devices.
  • The Hardware Model identifies the device type. If you get a lot of crashes from the same device model, it might mean that your app is not working properly on a specific model. In the log above, the device is an iPhone 4s.
  • Process is the name of the application. The number in the brackets is the process ID of the application at the time of crash.
  • The next few lines should be self-explanatory.

(2) Basic Information

This section gives you some basic information about the date and time of the crash, and the version of iOS running on the device. If you have a lot of crash logs coming from iOS 6.0, it might mean that your problem is specific to iOS 6.

(3) Exception

In this section, you can see the type of exception that was thrown at the time of the crash. You also get the exception code and the thread that threw the exception. Depending on the type of crash report, you may get some extra information in this section as well.

(4) Threads backtraces

This section provides the backtrace log for all threads in the app. Backtrace is a list of all active frames at the time of the crash. It gives you a list of function calls when the crash happened. Consider the line below:

2    XYZLib    0x34648e88    0x83000 + 8740

It is basically four columns:

  1. The frame number – in this case, 2.
  2. The name of the binary – in this case, XYZLib.
  3. The address of the function that was called – in this case, 0x34648e88.
  4. The fourth column is divided into two sub-columns, a base address and an offset. Here it is 0x83000 + 8740, where the first number points to the file, and the second points to the line of code in that file.

(5) Thread state

This section gives you the values in the registers at the time of crash. You don’t usually need this section, because the backtrace has already given you the information you need to find your problem.

(6) Binary images

This section lists all the binaries that were loaded at the time of the crash.

Demystification with Symbolication

When you first look at the backtrace in a crash log, it doesn’t make sense. You’re used to working with function names and line numbers, not a cryptic location like this:

6    Rage Masters    0x0001625c    0x2a000 + 30034

The process of converting from these hexidecimal addresses in the executable code to method names and line numbers is called symbolification.

When you get crash logs off of a device through Xcode’s Organizer window, they are automatically symbolicated after a few seconds. The symbolicated version of the above line is this:

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

For Xcode to symbolicate a crash log, it needs to have access to the matching application binary that was uploaded to the App Store, and the .dSYM file that was generated when that binary was built. This must be an exact match; otherwise, the report cannot be fully symbolicated.

So, it is essential that you keep each build distributed to users. When you archive your app before submission, Xcode stores your binary. You can find all of your archived applications in the Xcode Organizer under the Archivestab.

Xcode will automatically symbolicate all crash reports that it encounters, if it has the matching .dSYM and application binary that produced the crash report. If you are switching computers or creating a new account, make sure you move over all of those binaries and put them in the right place, where Xcode can find them.

Note: You must keep both the application binary and the .dSYM file to be able to fully symbolicate crash reports. You should archive these files for every build that you submit to iTunes Connect.

The .dSYM and application binary are specifically tied together on a per-build-basis, and subsequent builds, even from the same source files, will not interoperate with files from other builds.

If you use the Build and Archive command, the files will be placed in a suitable location automatically. Otherwise, any location searchable by Spotlight (such as your home directory) is fine.

Low Memory Crashes

Since low memory crash logs are slightly different than normal crash logs, this tutorial will address them separately. :]

When a low memory condition is detected on an iOS device, the virtual memory system sends out notifications asking applications to release memory. These notifications are sent to all running applications and processes, in an effort to reduce the total amount of memory in use.

If memory usage remains high, the system may terminate background processes to ease memory pressure. If enough memory can be freed, your application will continue to run and no crash report will be generated. Otherwise, your app will be terminated by iOS, and a low memory report will be generated.

In low memory crash logs, there are no stack traces for the application threads. Instead, the memory usage of each process is reported in terms of the number of memory pages. (At the time of writing, a memory page was 4KB in size.)

You will see jettisoned next to the name of any process terminated by iOS to free up memory. If you see it next to your application’s name, it means the application was terminated for using too much memory.

A low memory crash log looks something like this:

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 <cc527ca9b51937c5adbe035fe27a7b12> 9320 (jettisoned) (active)
mediaserverd <3d3800d6acfff050e4d0ed91cbe2467e> 255
dataaccessd <13d80b2e707acc91f9aa3ec4c715b9cc> 505
syslogd <8eddddc00294d5615afded36ee3f1b62> 71
apsd <32070d91b216d806973c8f1b1d8077a4> 171
securityd <b9e51062610d27f727c5119b8f80dcdf> 243
notifyd <591dd4dd804b4b8741f52335ea1fa4ab> 2027
CommCenter <b4b87526ae086bb62c982f1078f43f81> 189
SpringBoard <324939a437d1cca1fa4af72d9f5d0eba> 2158 (active)
accessoryd <8f21c8b376d16e2ccb95ed6d21d8317a> 91
configd <85efd41aceac34ccc0019df76623c7a9> 371
fairplayd <a2eaf736b3e07c7c9a2c82e9eb893555> 93
mDNSResponder <df1cd275e4ad434e0575990e8e1da4cb> 292
lockdownd <80d2bd44c0bcca273d48ce52010f7e65> 1204
launchd <a5988245aade809bf77576f1d9de42c5> 72

When you experience a low memory crash in your app, you should investigate memory usage patterns and how your app responds to low memory warnings. You can use the Allocations and Leaks Instruments to discover memory allocation issues and leaks. If you don’t know how to use Instruments to check for memory issues, you might want to check out this tutorial as a starting point.

And don’t forget virtual memory! The Leaks and Allocations Instruments do not track graphics memory. You need to run your application with the VM Tracker Instrument to see your graphics memory usage.

VM Tracker is disabled by default. To profile your application with VM Tracker, click the Instrument, and check theAutomatic Snapshotting flag or press the Snapshot Now button manually.

You will investigate a low memory crash log later on in this tutorial.

Exception Codes

Before you dive into some real-life crash scenarios, there’s one more bit about crash log content that warrants a brief introduction: those funny exception codes.

You can find the exception code in the Exception section of the report – section #3 in the above example. There are a few well-known exception codes that you might encounter often.

Usually, an exception code starts with some text, followed by one or more hexadecimal values, which are processor-specific codes that may give you more information on the nature of the crash. From these codes, you can tell if the application crashed due to a programming error, a bad memory access, or if the application was terminated for some other reason.

Here are some of the more common exception codes:

  • 0x8badf00d: Reads as “ate bad food”! (If you squint your eyes and replace the digits with alphabetic characters. :p) This code indicates that an application was terminated by iOS because a watchdog timeout occurred. Basically, the application took too long to launch, terminate, or respond to system events.

  • 0xbad22222: This code indicates that a VoIP application was terminated by iOS because it resumed too frequently.
  • 0xdead10cc: Read this one as “dead lock”! It indicates that an application was terminated by iOS because it held onto a system resource, like the address book database, while running in the background.
  • 0xdeadfa11: Another cute code – read it as “dead fall”! It indicates that an application was force-quit by the user. Per Apple, force quits happen when the user holds down the On/Off button until “slide to power off” appears, then holds down the Home button. As per Apple Documentation, a force quit event results in a 0xdeadfa11 exception code, presumably because the app became unresponsive.

Note: Remember that terminating a suspended app by removing it from the background task list does not generate a crash log. Once an app is suspended, it is eligible for termination by iOS at any time. So no crash log will be generated.

Swimming Time!

All right! You’ve got all the basic background information you need to dive and swim in the pool of crash logs, and fix some nasty bugs! Here is the basic scenario:

You have just started a new job at Rage-O-Rage LLC. The company has a top-selling app in the App Store, Rage Masters.

Your boss, Andy, comes to you and says that to get you started, he has decided to give you a list of different scenarios under which users have claimed the app crashes. Your job is to go through the scenarios and the symbolicated crash logs provided by users, find the bugs in the app, and fix them.

You can download the source code for the app from here.

Note: If you want to regenerate the crash reports for yourself, follow these instructions:

  1. Download the code and open the project file in Xcode.
  2. Connect an authorized iOS device with the correct provisioning profile.
  3. Select the iOS device as the target from the Xcode toolbar – not the Simulator – and build the app.
  4. As soon as you see the default screen (the full-screen image of the app) on the device, hit the stop button in Xcode.
  5. Close Xcode.
  6. Open the app directly on the device.
  7. Test the scenarios, and after you are finished, connect the device and get the crash logs via Xcode.

Scenario 1: Bad Code for Breakfast

From a user email: “Man, your app is a piece of junk! I downloaded it on my iPod Touch, iPhone and my son’s iPod Touch. On all devices, it crashed before running on the first try…”

Another email says, “I downloaded your app and it crashed. Very unhappy…”

Another email is more specific: “I can’t get your app to run. I downloaded it on all of my devices and on my wife’s. On all of them, it crashed on the first launch…”

OK, don’t take this lying down! Do any of these comments give you a hint? Take a look at the crash log:

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

Did you find the problem? The exception code is 0x000000008badf00d, and right after that, the report says:

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

This means that the app failed to launch in time, and the iOS watchdog terminated the app. Cool! You found the reason, but why (and more importantly, where) is it happening?

Look further down in the log. The convention is to read the backtrace log from the bottom up. The lowest frame (frame 25: libdyld.dylib) is the first call, then frame 24, Rage Masters, main (main.m:16) is called from there, and so on.

You are interested in frames that are related to your app’s code. So ignore system libraries and frameworks. The next line relevant to your code is:

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

The app failed in application:didFinishLaunchingWithOptions:, on line 35 in RMAppDelegate (RMAppDelegate.m:35). Open Xcode and take a look at that line:

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

Yep, there it is! A synchronous call to a web service?! On the main thread?! Inapplication:didFinishLaunchingWithOptions:?!! Who wrote this code?!

Network calls on the main thread makes kittens sad.

Anyway, it’s your job to fix it. This call should be asynchronous and, even better, executed from another part of the application, after application:didFinishLaunchingWithOptions: has returned YES.

It might require more extensive changes to move the call elsewhere. So for the moment, just take care of the code so that it doesn’t crash. You can always go back and implement a better design later. Replace the line of offending code from above (and the three lines following it) with an asynchronous version:

[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];
}];

Scenario 2: Button FUBAR

A user writes: “I can’t bookmark a rage master as my favorite. When I try to, the app crashes…”

Another user says, “Bookmarking doesn’t work… I go to detail info, tap on bookmark button and BOOM!”

The above complaints don’t say much, and there could be any number of causes for the issue they’ve identified. Take a look at the crash log:

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

The exception code is a SIGABRT. Usually, a SIGABRT exception is raised when an object receives an unimplemented message. Or to put it in simpler terms, there’s a call for a nonexistent method on an object.

Usually this won’t happen, since if you call method “foo” on object “bar”, the compiler will throw an error if the method “foo” doesn’t exist. But when you indirectly call a method using a selector, the compiler might not be able to determine whether or not the method exists on the object.

Back to the crash log. It indicates that the crashed thread is 0. This means that you’re probably looking at a situation where a method was called on an object on the main thread, where the object did not implement the method.

If you continue reading the backtrace log, you see that the only call related to your code is frame 22, main.m:16. That doesn’t help much. :[

Keep going up the framework calls, and there it is:

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

That isn’t your code. But at least it confirms that there was a call to an unimplemented method on an object.

Go to RMDetailViewController.m, since that’s where the bookmark button is implemented. Find the code that bookmarks the rage master:

-(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];
}

That looks OK, so check the storyboard (XIB file) and make sure that the button is hooked up correctly.

There it is! In MainStoryboard.storyboard, the button refers to bookmarkButtonPressed: instead of bookmarkButtonPressed (note the final colon indicating that the method expects a parameter). To fix this, replace the signature of the method above with this:

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

Of course, you could also simply remove the incorrect connection in the XIB file and reconnect to the method, so that the method signature is correct in the XIB file. Either way works.

And that’s another crash fixed. You’re getting pretty good at this. :]

Scenario 3: Another Bug On the Table

Another user complains saying, “I can’t delete bookmarked masters from the bookmarks view…” And another email about the same issue reads, “If I try to delete a master in bookmarks, the app crashes…”

By now, you are used to these emails not being very helpful. To the crash logs!

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

This looks very similar to the previous crash log. It’s another SIGABRT exception. Perhaps you wonder if it’s the same issue: sending a message to an object that has not implemented the method?

Let’s traverse through the backlog and see what methods were called. Start from the bottom. The last call to your code in Rage Masters is at frame 6:

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

Well, this is a UITableViewDataSource method. Huh?! You are pretty sure Apple has implemented this method – you can override it, but it is not like it hasn’t been implemented. Plus, it is an optional delegate method. So the problem isn’t a call to an unimplemented method.

Take a look at the frames after that call:

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

In frame 5, UITableView is calling another method of its own, deleteRowsAtIndexPaths:withRowAnimation: and then _endCellAnimationsWithContext: is called, which looks like an internal Apple method. Then the Foundation framework raises an exception, handleFailureInMethod:object:file:lineNumber:description:.

Putting the above together with the user complaints, it looks as if you are dealing with a buggy deletion procedure in UITableView. Go to Xcode. Do you know where to go? Can you tell from the crash log? It’s line 68 inRMBookmarksViewController.m:

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

Can you tell where the problem is? I’ll wait while you take a look.

You got it! How about the data source? The code deletes a row in the table view, but doesn’t change the data source behind it. To fix this, replace the above method with the following one:

-(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];
}

That should do it! Take that, you nasty bug!! BIFF!!! BANG!!!! CRASH!!!!!

Scenario 4: Leggo Those Licks!

The email says, “My app crashes when a rage master is licking a lollipop…” Another user writes, “I tell the rage master to lick the lollipop few times, and then the app crashes!”

Here’s the crash log:

Incident Identifier: 081E58F5-95A8-404D-947B-5E104B6BC1B1
CrashReporter Key: 5a56599d836c4f867f6eec76afee451bf9ae5f31
Hardware Model: iPhone4,1
OS Version: iPhone OS 6.0 (10A403)
Kernel Version: Darwin Kernel Version 13.0.0: Sun Aug 19 00:28:05 PDT 2012; root:xnu-2107.2.33~4/RELEASE_ARM_S5L8940X
Date: 2012-11-03 13:39:59 -0400
Time since snapshot: 4353 ms
 
Free pages: 968
Active pages: 7778
Inactive pages: 4005
Throttled pages: 92319
Purgeable pages: 0
Wired pages: 23347
Largest process: Rage Masters
 
Processes
Name <UUID> rpages recent_max [reason] (state)
 
lsd <6a9f5b5f36b23fc78f87b6d8f1f49a9d> 331 331 [vm] (daemon) (idle)
afcd <b0aff2e7952e34a9882fec81a8dcdbb2> 141 141 [vm] (daemon) (idle)
itunesstored <4e0cd9f873de3435b4119c48b2d6d13d> 1761 1761 [vm] (daemon) (idle)
softwareupdatese <2bc4b5ae016431c98d3b34f81027d0ae> 311 311 [vm] (daemon) (idle)
Amazon <4600481f07ec3e59a925319b7f67ba14> 2951 2951 [vm] (suspended)
accountsd <ac0fce15c1a2350d951efc498d521ac7> 519 519 [vm] (daemon) (idle)
coresymbolicatio <edba67001f76313b992056c712153b4b> 126 126 [vm] (daemon) (idle)
Skype <504cf2fe60cb3cdea8273e74df09836b> 3187 3187 [vm] (background)
MobileMail <bff817c61ce33c85a43ea9a6c98c29f5> 14927 14927 [vm] (continuous)
MobileSMS <46778de076363d67aeea207464cfc581> 2134 2134 [vm] (background)
MobilePhone <3fca241f2a193d0fb8264218d296ea41> 2689 2689 [vm] (continuous)
librariand <c9a9be81aa9632f0a913ce79b911f27e> 317 317 [vm] (daemon)
kbd <3e7136ddcefc3d77a01499db593466cd> 616 616 [vm] (daemon)
tccd <eb5ddcf533663f8d987d67cae6a4c4ea> 224 224 [vm] (daemon)
Rage Masters <90b45d6281e934209c5b06cf7dc4d492> 28591 28591 [vm] (frontmost) (resume)
ptpd <04a56fce67053c57a7979aeea8e5a7ea> 879 879 (daemon)
iaptransportd <f784f30dc09d32078d87b450e8113ef6> 230 230 (daemon)
locationd <892cd1c9ffa43c99a82dba197be5f09e> 1641 1641 (daemon)
syslogd <cbef142fa0a839f0885afb693fb169c3> 237 237 (daemon)
mediaserverd <80657170daca32c9b8f3a6b1faac43a2> 4869 4869 (daemon)
dataaccessd <2a3f6a518f3f3646bf35eddd36f25005> 1786 1786 (daemon)
aosnotifyd <d4d14f2914c3343796e447cfef3e6542> 549 549 (daemon)
wifid <9472b090746237998cdbb9b34f090d0c> 455 455 (daemon)
SpringBoard <27372aae101f3bbc87804edc10314af3> 18749 18749
backboardd <5037235f295b33eda98eb5c72c098858> 5801 5801 (daemon)
UserEventAgent <6edfd8d8dba23187b05772dcdfc94f90> 601 601 (daemon)
mediaremoted <4ff39c50c684302492e396ace813cb25> 293 293 (daemon)
pasteboardd <8a4279b78e4a321f84a076a711dc1c51> 176 176 (daemon)
springboardservi <ff6f64b3a21a39c9a1793321eefa5304> 0 0 (daemon)
syslog_relay <45e9844605d737a08368b5215bb54426> 0 0 (daemon)
DTMobileIS <23303ca402aa3705870b01a9047854ea> 0 0 (daemon)
notification_pro <845b7beebc8538ca9ceef731031983b7> 169 169 (daemon)
syslog_relay <45e9844605d737a08368b5215bb54426> 0 0 (daemon)
ubd <74dc476d1785300e9fcda555fcb8d774> 976 976 (daemon)
twitterd <4b4946378a9c397d8250965d17055b8e> 730 730 (daemon)
configd <4245d73a9e96360399452cf6b8671844> 809 809 (daemon)
absinthed.N94 <7f4164c844fa340caa940b863c901aa9> 99 99 (daemon)
filecoordination <fbab576f37a63b56a1039153fc1aa7d8> 226 226 (daemon)
distnoted <a89af76ec8633ac2bbe99bc2b7964bb0> 137 137 (daemon)
apsd <94d8051dd5f5362f82d775bc279ae608> 373 373 (daemon)
networkd <0032f46009f53a6c80973fe153d1a588> 219 219 (daemon)
aggregated <8c3c991dc4153bc38aee1e841864d088> 112 112 (daemon)
BTServer <c92fbd7488e63be99ec9dbd05824f5e5> 522 522 (daemon)
fairplayd.N94 <7bd896bd00783a48906090d05cf1c86a> 210 210 (daemon)
fseventsd <996cc4ca03793184aea8d781b55bce08> 384 384 (daemon)
imagent <1e68080947be352590ce96b7a1d07b2f> 586 586 (daemon)
mDNSResponder <3e557693f3073697a58da6d27a827d97> 295 295 (daemon)
lockdownd <ba1358c7a8003f1b91af7d5f58dd5bbe> 389 389 (daemon)
powerd <2d2ffed5e69638aeba1b92ef124ed861> 174 174 (daemon)
CommCenter <1f425e1e897d32e8864fdd8eeaa803a8> 2212 2212 (daemon)
notifyd <51c0e03da8a93ac8a595442fcaac531f> 211 211 (daemon)
ReportCrash <8c32f231b2ed360bb151b2563bcaa363> 337 337 (daemon)

This log is very different from what you’ve seen so far!

This is a low memory crash log from iOS 6. As discussed earlier, low memory crash logs are different from other types of crash logs because they don’t point to a specific file or line of code. Instead, they paint a picture of the memory situation on the device at the time of the crash.

The header, at least, is similar to that of other crash logs: it provides the Incident Identifier, CrashReporter Key, Hardware Model, OS Version, and some other information.

The next section is specific to low memory crash logs:

  • Free pages refers to available memory. Each page is approximately 4KB, so in the log above, available memory is about 3,872 KB (or 3.9 MB).
  • Purgeable pages are those parts of memory that can be purged and reused. In the log above, it is 0KB.
  • Largest process refers to the application that was using most of the memory at the time of the crash, which in this case is your app!
  • Processes gives you a list of processes, along with their memory usage at the time of the crash. You are given the name of the process (first column), the unique identifier of the process (second column), and the number of pages being used by the process (third column). In the last column, State, you can see the state of each app. Usually, the app that caused the crash is the app with the frontmost state. Here it is Rage Masters, which was using 28591 pages (or 114.364 MB) – that’s a lot of memory!

Usually, the largest process and the frontmost app are the same, and are also the process that has caused the low memory crash. But you may see some instances where the largest process and the frontmost app are not the same. For example, if the largest process is SpringBoard, ignore it, because SpringBoard is the process that shows the apps on the home screen, the popups that appear when you double tap the home button, etc. and is always active.

When low memory situations happen, iOS sends a low memory warning to the active application and terminates background processes. If the frontmost app still continues to grow in memory, iOS jettisons it.

To find the reason for the low memory issues, you need to profile your app using Instruments. You won’t be doing that here, since we already have a tutorial for that. :] Instead, you’ll take a shortcut and just respond to the low memory warning notification that the app receives to solve this particular crash.

Switch to Xcode and go to RMLollipopLicker.m. This is where the lollipop licker view controller is implemented. Take a look at the source code:

#import "RMLollipopLicker.h"
 
#define COUNT 20
 
@interface RMLollipopLicker ()
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UILabel *lickedTimeLabel;
@end
 
@implementation RMLollipopLicker {
NSOperationQueue *queue;
NSMutableArray *lollipops;
}
 
#pragma mark - Life cycle
 
- (void)viewDidLoad {
[super viewDidLoad];
 
self.progressView.progress = 0.0;
self.label.text = [NSString stringWithFormat:@"Tap on run and I'll lick a lollipop %d times!", COUNT];
self.lickedTimeLabel.text = @"";
 
lollipops = [[NSMutableArray alloc] init];
queue = [[NSOperationQueue alloc] init];
}
 
- (void)lickLollipop {
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"Lollipop" withExtension:@"plist"];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:fileURL];
NSString *lollipop = [dictionary objectForKey:@"Lollipop"];
[lollipops addObject:lollipop];
}
 
#pragma mark - IBActions
 
- (IBAction)doneButtonPressed:(id)sender {
 
[self dismissViewControllerAnimated:YES completion:nil];
}
 
- (IBAction)runButtonPressed:(id)sender {
 
[sender setEnabled:NO];
[queue addOperationWithBlock:^{
 
for (NSInteger i = 0 ; i <= COUNT ; i++) {
[self lickLollipop];
 
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
 
self.label.text = [NSString stringWithFormat:@"Licked a strawberry lollipop %d time(s)!", i];
self.lickedTimeLabel.text = [NSString stringWithFormat:@"Licked the same lollipop %d time(s)!", lollipops.count-1];
self.progressView.progress = (float)(i/COUNT);
 
if (i >= COUNT) {
self.label.text = [NSString stringWithFormat:@"Tap on run and I'll lick a lollipop %d times!", COUNT];
self.progressView.progress = 0.0;
[sender setEnabled:YES];
}
}];
}
}];
 
}
 
@end

When the user taps the run button, the app starts a background operation, calls lickLollipop a number of times, and then updates the UI to reflect the number of licks. lickLollipop reads a big NSString from a property list (PLIST) and adds it to an array. This data is not crucial, and can be recreated without affecting the user experience.

It’s a good habit to take advantage of every situation where you can purge data and recreate it without adversely affecting the user experience. This frees up memory, making low memory warnings less likely.

So how can you improve the code here? Implement didReceiveMemoryWarning and get rid of the data in lollipops as follows:

-(void)didReceiveMemoryWarning {
[lollipops removeAllObjects];
[super didReceiveMemoryWarning];
}

That should be it!

Where to Go From Here?

Hooray, you made it through all four crash scenarios! Your app works much better and you learned some important debugging skills along the way. :]

You can download the improved project here.

How did you like demystifying iOS crash logs? I encourage you to continue your learning by trying this out in your own apps – and hopefully solving the crashes and making your apps more robust!

If you have any questions or comments on this tutorial or crash logs in general, please join the forum discussion below!


This is a blog post by Soheil Moayedi Azarpour, an independent iOS developer. You can also find him on Google+.

Soheil Azarpour

Soheil Moayedi Azarpour is an independent iOS developer. He’s worked on iOS applications for clients as well as his own personal apps. You can find him on TwitterGitHubStack Overflow and connect on LinkedIn.

Demystifying iOS Application Crash Logs的更多相关文章

  1. Understanding and Analyzing Application Crash Reports

    Introduction When an application crashes, a crash report is created and stored on the device. Crash ...

  2. IOS Application Security Testing Cheat Sheet

    IOS Application Security Testing Cheat Sheet    [hide]  1 DRAFT CHEAT SHEET - WORK IN PROGRESS 2 Int ...

  3. iOS解析crash日志:

    iOS解析crash日志:我们在ios开发中会碰到的很多crash问题,如果Debug调试模式的话,我们可以往往很容易的根据log的输出定位到导致crash的原因,但对于已经上线的应用,或者是rele ...

  4. [转]Running JavaScript in an iOS application with JavaScriptCore

    原文:https://www.infinum.co/the-capsized-eight/articles/running-javascript-in-an-ios-application-with- ...

  5. iOS 调试 crash breakpoint EXC_BAD_ACCESS SIGABRT

    原文地址:iOS 调试 crash breakpoint EXC_BAD_ACCESS SIGABRT作者:流年若离殇 在调试程序的时候,总是碰到crash的bug,而且一追踪就是一些汇编的代码,让人 ...

  6. iOS 苹果官方 Crash文件分析方法 (iOS系统Crash文件分析方法)

    时间2013-08-20 12:49:20 GoWhich原文  http://www.gowhich.com/blog/view/id/343 苹果官方 Crash文件分析方法 (iOS系统Cras ...

  7. iOS Application Project与OS X Application Project对于plist使用的区别

    前几天因为在开源中国看到一个求源代码的问题: 模拟一个动物园系统MyZoo 1.动物园里面有三种动物:Panda,Elephant,Kangaroo 2.三种动物都有一定的数量(不止一只) 3.动物有 ...

  8. Persisting iOS Application Data in SQLite Database Using FMDB

    In previous articles we have utilized NSUserDefaults and .NET web services to persist iPhone data. N ...

  9. iOS Application Security

    文章分A,B,C,D 4个部分. A) iOS Application Security 下面介绍iOS应用安全,如何分析和动态修改app. 1)iOS Application security Pa ...

随机推荐

  1. Social Media Addiction【社交媒体上瘾】

    Social Media Addiction Children as young as ten are becoming dependent on social media for their sen ...

  2. 论如何入门地使用vscode

    微软大法好啊 这货更像是个gedit 以下内容只适合Oiers使用 本文档只适合新手引导的阶段使用 下载 这个是链接 可见这东西是和Emacs一样跨系统的 不知道为什么下载速度贼快 配置 还记得我们用 ...

  3. 为 DirectAccess 设计 DNS 基础结构

    TechNet 库Windows ServerWindows Server 2008 R2 und Windows Server 2008浏览 Windows Server 技术NetworkingD ...

  4. PJMEID学习之视频的捕捉与播放

    pjmedia是pjsip的视频部分,官网明确提示,要想使用pjmedia离不开directshow/sdl/ffmpeg这三个库. 软件版本的限制: ffmpeg不能高于1.25.(建议下载1.01 ...

  5. 剑指Offer - 九度1360 - 乐透之猜数游戏

    剑指Offer - 九度1360 - 乐透之猜数游戏2014-02-05 19:54 题目描述: 六一儿童节到了,YZ买了很多丰厚的礼品,准备奖励给JOBDU里辛劳的员工.为了增添一点趣味性,他还准备 ...

  6. javascript 数组的常用方法总结

    前言 主要讨论一下数组的方法,     1.splice和slice的区别     2.pop和push     3.shift和unshift     4.join     5.forEach(es ...

  7. ASP.NET Core [2]:Middleware-请求管道的构成(笔记)

    原文链接:http://www.cnblogs.com/RainingNight/p/middleware-in-asp-net-core.html 中间件处理请求主要分为三个阶段:1. 中间件的注册 ...

  8. HDU 4661 Message Passing ( 树DP + 推公式 )

    参考了: http://www.cnblogs.com/zhsl/archive/2013/08/10/3250755.html http://blog.csdn.net/chaobaimingtia ...

  9. ocrosoft Contest1316 - 信奥编程之路~~~~~第三关 问题 P: 【数组】1234方阵(phalanx)

    http://acm.ocrosoft.com/problem.php?cid=1316&pid=15 题目描述 编程打印如下规律的n*n方阵.输入n,按规律输出方阵. 方阵规律如下图:使左对 ...

  10. Vue.js入门(一)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta htt ...