PodSearch

Under the Radar

80: Debugging

 

00:00:00   Welcome to Under the Radar, a show about independent iOS app development. I'm Marco Arment.

00:00:06   And I'm David Smith. Under the Radar is never longer than 30 minutes, so let's get started.

00:00:11   So today we wanted to dive into everybody's favorite pastime as a developer, and that is debugging.

00:00:20   It is a topic that I think we probably... it's kind of weird in some ways, because I think I probably spend

00:00:27   at least half, if not a majority of my time in development debugging code that I write.

00:00:33   Yet, in most of my actual formal preparation for being a software engineer, there was very, very little time and attention spent on it.

00:00:42   Almost all of my classes and things were all focused on the actual development and the architecture side of things,

00:00:49   whereas the reality is we spend most of our time fixing the problems and issues that arise in our apps.

00:00:55   And these can range from all kinds of different levels. There's the while you're developing debugging process,

00:01:02   where you write some code, you hit build and run, and you see if it works, and if it doesn't, you try and work out why.

00:01:10   There's the more subtle, something happens every now and then, and you're not really sure why, and you try and track that down.

00:01:19   And then there's the, you know, you're getting crash reports or bug reports from customers in the field, and then you have to try and track those down,

00:01:26   which is even more exciting and challenging.

00:01:30   But in all of them, I think there's an element of, you know, debugging is essentially, you have... you sort of identify that there's some problem,

00:01:40   you then try and reproduce that problem in a way that you can then observe that problem,

00:01:47   then you try and isolate where in the actual program that is happening, and then you can try and fix it.

00:01:54   And you kind of go through this cycle of, you identify it, you reproduce it, you isolate it, and then you can fix it.

00:02:02   And you kind of cycle through that, or at least that's the process that I tend to take,

00:02:05   because if I don't go through each of those steps, I find that it's very hard to actually make progress.

00:02:10   You know, if you can't... you know, some bugs really are, by their nature, kind of hard to reproduce.

00:02:15   And if that's the case, like, you know, it's still a bug and it's still problematic,

00:02:19   but if I get a bug report that I can't reproduce in any way, it's just as the same in some ways as not having that bug report in the first place,

00:02:27   because I can't do anything with that information if I can't reproduce it.

00:02:31   So, you know, my first step is always, let's reproduce this, and then, you know, try and find the part of code.

00:02:37   And I think, having been a professional developer for enough years now, I feel like I have a better sort of sense for where the bugs might be hiding.

00:02:46   You know, it's like, I've seen every, you know, not every bug, but I've seen a lot of bugs before,

00:02:52   and you kind of get this sense of like, "Huh, this looks like this is an off-by-one error.

00:02:59   This looks like this is a cache invalidation error. This looks like I'm writing something incorrectly here,

00:03:05   or I forgot a case in a switch statement." Like, you kind of get this feeling for it, and then you can kind of go running around trying to find it.

00:03:12   And then, of course, there's also, I guess, the other kind of debugging, which is even super fun,

00:03:16   which is where there's a bug in the OS that you, you know, where your code is all doing the right thing,

00:03:23   but then you're getting weird results, you know, from the OS.

00:03:27   And so, that's an even more fun line of debugging where you kind of get this sense of, at a certain,

00:03:32   you know, you keep going down and farther and farther down the rabbit hole until eventually you're like,

00:03:36   hit a brick wall at the end of it, and you're like, "All right, well, I guess I file a bug."

00:03:40   Yeah, and that's, you know, it's often easy for us to jump to, "Well, this must be a bug in the OS,"

00:03:49   or my favorite one, "This must be a bug in the compiler," or something like that, when we can't figure it out.

00:03:54   That is, for the vast majority of bugs that we face as programmers, that is almost never the case.

00:04:01   It, like, first of all, you are unlikely to ever have a bug in the compiler actually affect you,

00:04:06   and, you know, as you go up the stack of, you know, things that you're relying on, like, the OS does have bugs.

00:04:13   Libraries that you're using will have bugs, but if you run into something in your app that seems like a bug,

00:04:21   and you're tempted to blame the OS or a library or something like that,

00:04:25   really make sure that you can't figure out that it's your bug first, because chances are very, very good.

00:04:32   It's probably your bug. The vast majority of the time of those cases, it will really be your bug.

00:04:38   It's only a question of whether you can find it, but don't just say, "Oh, well, it's a bug in the OS.

00:04:43   I guess I gotta just deal with this," or move on or yell at them.

00:04:46   Really make sure it's not your bug first, because, again, the odds are strongly in favor of it being yours.

00:04:52   Yeah, because the reality is, like, most of our time, at least in my experience, dealing with bugs,

00:05:00   it's almost always something I forgot to do or a case that I forgot to consider when I was originally doing it.

00:05:07   Not necessarily out of laziness, even. Sometimes it's that. Sometimes you have the always classic,

00:05:14   like, you know, you see an example where it's like, if error doesn't equal nil, then do something with it.

00:05:21   And then if you don't actually do anything, when eventually you do run into the situation where the error is not nil,

00:05:26   then all of a sudden things break, and there's that kind of laziness.

00:05:29   But more often, it's just like you forgot to do something, and then it comes back to bite you.

00:05:34   And I feel like the--I think there was a--maybe there was an XKCD cartoon about this,

00:05:42   where it's sort of the stages of debugging, where you go from, like, it's like,

00:05:46   oh, you get a bug report, and you're like, oh, no, that can't be right. I'm sure my code is good.

00:05:51   And then you're kind of like, oh, no, it's actually broken. And then you go to the, like, how did this ever work phase?

00:05:57   You're like, how on earth did this even work? Because it's amazing how, when you look back at your own code,

00:06:03   you know, this is the code that you wrote with your own hands on a keyboard.

00:06:07   You put this into Xcode. You come back and look at it, and you're like, how on earth? What am I doing?

00:06:13   And I feel like that is the hardest part, or why debugging is so hard, is you have to recreate all of this sort of state

00:06:23   and information that went into the creation of the code, hold that in your mind,

00:06:27   and then run it through a scenario that's happening, and to then see why it's doing something weird.

00:06:33   And very often, I find, like, the hardest thing is to try and understand even what the code is trying to do in the first place,

00:06:39   or why there's this weird exception that was introduced there years or decades ago,

00:06:46   and you're like, huh, I wonder why I did that. I don't know anymore. And that makes debugging really, really hard.

00:06:53   - Yeah, and this is, it's also a major argument for keeping things simple and not that clever in the first place.

00:07:01   As we talked about, I think, a while ago now, I try to avoid overly clever code styles,

00:07:08   things like using really dense short hands or really clever mechanics, things like generics in Swift, that kind of thing.

00:07:19   You kind of use overly clever complex language features or constructs where it looks really cool,

00:07:27   and you feel really smart when you do it, but when you have to come back and debug code in six months,

00:07:33   and you have totally forgotten what led you to write that, and you've forgotten the cleverness behind it,

00:07:39   and you have to figure it out by looking at it or figuring it out by its effects,

00:07:44   it's often very, very hard to do that. And you often, as a programmer, you might eventually hear the wisdom of,

00:07:52   like, write code for someone else to read, but really what you should be doing is writing code for yourself in three weeks to read,

00:08:01   because you will have probably forgotten by then why you did what you did.

00:08:05   So if you keep things simple from the beginning and not be too overly clever with the way you write things

00:08:11   and make things straightforward to read and to kind of self-document, then you will be doing yourself a huge favor.

00:08:19   Documentation, separate documentation, can help with some of these things, but I've found in practice that,

00:08:26   maybe it's just me, I don't document things that well. I try to just write things that are obvious when you look at them,

00:08:32   and that just have good names and straightforward approaches, and that tends to work very well.

00:08:38   That can never be out of date, because it's the code itself.

00:08:41   And then for minimizing bugs to begin with, I think it's, again, very important not to be too clever and to realize,

00:08:51   you know, for me, many of my bugs come from quick workarounds or quick hacks that I do when I encounter some other bug.

00:09:00   And so one great example of this that I'm sure many iOS programmers face is,

00:09:06   whenever you are doing something in, like, a launch loop or in a UI, you know, response loop or something,

00:09:15   and there's some weird behavior that, or some weird precondition that you can't quite accommodate in a straightforward way.

00:09:23   So you just dispatch async to the next run loop of the main queue, and you say,

00:09:28   "All right, dispatch async onto main queue, do this thing on the next run."

00:09:32   And that's a really nice, quick way to avoid a lot of, like, you know, weird complexity or obvious bugs as you're developing.

00:09:42   But I have found in almost every case that using a dispatch async is almost always a bad idea,

00:09:50   like when you're just, like, deferring work till later on the main queue or something.

00:09:54   Like, obviously there are uses where it's fine, but, like, if that's your approach to solve a problem,

00:09:59   then you get into concurrency bugs and weird conditions there that are really hard to fix and diagnose and reproduce.

00:10:10   And, again, it's like one of those areas where it seems like a clever thing when you're writing it,

00:10:18   and in the case of, like, bug avoidance, like, it seems like, "Oh, this will be a quick fix for this weird little bug.

00:10:23   I'll just wrap it in dispatch async and that will fix the problem."

00:10:28   But then you introduce all these weird other problems that might be non-obvious, and those become very hard to debug.

00:10:36   So, in addition to keeping code simple as you write it, for the purposes of debugging,

00:10:42   I also would strongly recommend avoiding concurrency if you can and avoiding playing with threads and queues

00:10:50   when not necessary or when not warranted.

00:10:54   Yeah, because I feel like what you're doing there, I mean, and I'm very guilty of that.

00:10:57   Like, there have, I have done the, you know, dispatch async fix probably a hundred times,

00:11:03   two hundred times in my career, like, all the time.

00:11:06   And I cannot overstate how many bugs that I've had to fix end up being a poorly used dispatch async.

00:11:14   Yeah, because I think the reality is what you're doing, and this is maybe a maturity thing,

00:11:19   but it's the understanding that, like, when I do that now, I understand that I'm not fixing the problem.

00:11:25   I'm changing the bug from whatever it is, say I have a bug that happens, you know,

00:11:30   one out of every hundred times the app runs.

00:11:33   And, you know, that's a problem, you know, one percent of users are hitting this issue.

00:11:36   I wrap it in dispatch async. What I'm doing is not removing that problem.

00:11:41   What I'm doing is changing it into a, like, one in ten thousand bug, which is better,

00:11:46   but also incredibly difficult now to actually, like, properly fix and diagnose and reproduce, etc., etc.

00:11:54   Like, it is the kind of fix where it isn't actually solving it. It's just making it less likely.

00:11:59   Which sometimes, you know, pragmatically, I've, you know, I'm sure there are many places in, you know,

00:12:03   my shipping apps where I have that, and it's just one of, it's like a conscious choice that you're just like,

00:12:07   "Well, the actual fix is too hard or would require a massive refactoring or some kind of, you know,

00:12:13   thing where you can make this trade-off in choice."

00:12:16   And you say, you know, like, "I'm just going to fix this by patching it."

00:12:19   And I know that what I'm doing is going to cause a problem for me down the road.

00:12:22   I know that, you know, there is, this bug is, hasn't magically disappeared as a result of this,

00:12:27   but, like, it's a conscious choice. And that's a balance.

00:12:31   But, yeah, ultimately, you know, most, when you're writing the code, you're kind of trying to do your future self a favor,

00:12:36   as much as you can. Because I think one of the things that I struggle with, too, when I'm writing code

00:12:41   is the realization that all of these things that seem obvious to me when I'm writing the code,

00:12:47   because I'm in a mindset where I'm focused entirely on whatever the solving this problem is.

00:12:52   Like, there's all these things that just seem obvious. Like, of course, you know, like, this is the way this works,

00:12:56   you know, that's why this is set up this way, this value will, you know, will always transform from this to this.

00:13:01   Like, there's all these things that seem obvious. And then, in the future, they're entirely non-obvious.

00:13:07   And the thing, the biggest difficulty I have with things like documentation

00:13:12   is that it's kind of impossible to know what your future self is going to want to know,

00:13:19   because everything seems obvious and kind of trivial in the present.

00:13:26   And so, like, I think I'm kind of like, I tend to only document code in my apps that,

00:13:32   where I'm doing something that is clearly weird or like, strange, you know,

00:13:37   where you have some kind of magic number that you have to introduce into something,

00:13:41   where it's like, I know, why am I multiplying this value by 1.632?

00:13:46   There's a reason for that, and if there is, like, you know, that's the kind of thing that you write down.

00:13:50   But otherwise, you end up with, like, writing your program twice if you're, you know, overly documenting it.

00:13:54   So, as much as when I'm debugging, I kind of wish my past self had written all this stuff down,

00:14:00   the reality is my past self wouldn't actually know what I need to know right now,

00:14:04   because if they did, they could have just written, you know, made the code more obvious or clear in the first place, probably.

00:14:10   Or at least that's what I tell myself.

00:14:13   We respond to this week by Pingdom.

00:14:16   Start monitoring your websites and servers today at pingdom.com/radar.

00:14:21   You got a 14-day free trial, and when you use code RADAR at checkout, you got 20% off your first invoice.

00:14:26   Pingdom is a wonderful monitoring service.

00:14:30   They are focused on making the web faster and more reliable for everyone who has a site,

00:14:35   because they offer powerful, easy-to-use monitoring tools and services.

00:14:39   So, if you're a Pingdom user, you can monitor the availability and performance of your servers,

00:14:43   your databases, or your website, and it's so easy to do.

00:14:47   They test for more than 70 global test servers, and they can emulate visits to your site as often as every minute.

00:14:54   And they can do cool things like cookies and logins and everything else, too.

00:14:57   Because, you know, these days, websites are becoming more and more sophisticated,

00:15:00   and very often include multiple different parts with several different dependencies.

00:15:04   Maybe you depend on an external service, or maybe only the login screen does this one thing in your code.

00:15:09   And you need to check all these things, and you can do all that with Pingdom, and loads more in addition to that, too.

00:15:15   They can make it possible to monitor the availability of all these key interactions people have with your site.

00:15:20   Because, you know, a lot of times it's more complex than your site is up or your site is down.

00:15:25   So, stuff breaks on the internet all the time. Pingdom knows this.

00:15:29   They detect around 13 million outages a month.

00:15:32   So, regardless of whether you have a small website or you're managing a whole infrastructure,

00:15:36   it is very important to monitor the availability and performance.

00:15:39   And Pingdom, when it goes down, they alert you in any way you want.

00:15:44   You can have text messages, you can have push notifications, emails.

00:15:47   It's so configurable. We've even used it in the past to monitor other people's websites.

00:15:52   Like, David and I have both independently used it to monitor the WWDC page for changes.

00:15:58   Like, in the past, when it was really important to know when WWDC tickets went on sale.

00:16:01   I've used Pingdom for a very long time. Highly recommended.

00:16:04   Check it out today and you will be the first to know when your site is down or being too slow.

00:16:10   So, go to Pingdom.com/Radar for a 14-day free trial and use code radar to get 20% off your first invoice.

00:16:18   Thank you very much to Pingdom for their support of this show and Relay FM.

00:16:21   So, the other thing that's probably worth diving into a little bit, too, is the way in which we actually do debugging.

00:16:29   Because maybe it's a shocking revelation, but I am, I believe, what is technically called a caveman debugger.

00:16:38   The printf debugging?

00:16:40   I do all of my debugging using, you know, nslog and the console, which I think is often disparagingly referred to as caveman debugging, which is fine.

00:16:51   I'm sure there are better ways to do that.

00:16:53   I mean, every now and then I'll go to a WWDC session where they're talking about all the crazy things you can do in Xcode, where you can set conditional breakpoints and all this kind of really clever stuff.

00:17:04   And in the end, what I always do is just, I end up just nslogging tons of data to the console.

00:17:11   Because it's one of those things where most of what debugging is trying to do is, it's like you're trying to unwind the state machine that is your app.

00:17:20   Where you're trying to work out, it's like, your app is in this problem, you're in a good state, then something happens and you're in a bad state.

00:17:27   Like that is, you know, at a very high level, that is what a bug is.

00:17:30   And what you're trying to do is identify that, you know, at what point it's going from that good state to that bad state.

00:17:37   Like what is happening there?

00:17:39   I mean, I've been professionally developing for 17 years now or something.

00:17:44   And I still, like, the most effective and powerful tool is just nslogs everywhere.

00:17:49   And you get kind of good at it.

00:17:52   Like, I can identify and find things fairly quickly this way because you kind of get the sense of, you know,

00:17:57   A, you're trying to isolate where in the code it's happening.

00:18:00   And so you'll end up putting nslogs, you know, in different parts of your app that you think might be part of it

00:18:06   and trying to work out which code is getting hit when you do it.

00:18:11   And then something that I also find really helpful is when I format my nslogs as tab-delimited text.

00:18:21   So like often I'll have several variables that I'm trying to observe and watch.

00:18:25   And I nslog those as a tab-delimited set of strings because then I can take the output from the console

00:18:33   and I can copy-paste that into Excel and it'll put it into columns for me.

00:18:37   That's interesting.

00:18:39   And you can take that data and then look at it over multiple runs.

00:18:44   It's a way to actually make it into something that's useful.

00:18:48   At least for me, in the way that my mind works, that's how I debug the best.

00:18:53   I'm taking something out of the console, looking at it, often in Excel, and then kind of finding the pattern,

00:19:00   finding the thing, and then I can actually go and fix it.

00:19:03   And I don't know if that works for me.

00:19:07   And sometimes I feel bad about it.

00:19:11   I feel like they have all these really powerful and clever tools.

00:19:13   But the reality is, I think the hardest part too is that so much of debugging is...

00:19:20   The hardest part is often the reproduction part.

00:19:23   And getting everything, it's hard to observe these issues sometimes.

00:19:28   And so the nice thing about a console log is that you can have those running without being attached to the debug server, for example.

00:19:36   You can create these situations where these things happen.

00:19:41   Sometimes it gets really awkward to have your app attached to the debugger.

00:19:46   Because sometimes when it is attached to the debugger, the bug goes away.

00:19:50   There's lots of weird situations and issues where NSLog debugging is the best.

00:19:56   But I'm very aware that by doing it that way, I'm not taking advantage of more sophisticated tools.

00:20:02   But I know for myself that's what works.

00:20:05   There's lots of different kinds of bugs.

00:20:09   And a lot of these different methods are more or less suitable to different ones.

00:20:13   For example, NSLog debugging, which I do a lot myself, is not necessarily the best option if you have a nitty-gritty code level bug.

00:20:26   This function isn't always returning the right value.

00:20:28   For that, I prefer to use conditional breakpoints.

00:20:30   So I can see whenever this value is something out of whack, stop here and then I will inspect the stuff around it.

00:20:35   But NSLog debugging is a lot more helpful for things like larger systems interacting with each other,

00:20:43   any kind of large operations, concurrency, things where you're not really sure where the bugs might be,

00:20:50   but you just want to get a good idea of what is going on in the system.

00:20:54   NSLog debugging is also very, very helpful in situations in which the debugger itself is impractically slow to do things.

00:21:04   So a lot of Swift context, this is the case from what I've heard.

00:21:07   It's also very much the case when you're debugging on device on the watch.

00:21:12   Because then the round trip between your computer running Xcode and the phone and the slow watch hardware,

00:21:19   trying to break watch apps with breakpoints and inspect local variables around and running things in the debugger command line,

00:21:27   is so slow when doing things on the watch that sometimes it's better off to just do something,

00:21:32   do it a different way, do printout debugging or whatever else.

00:21:36   For me, I also debug very differently if I'm looking at a crash report versus some kind of behavior in the app that I'm catching live.

00:21:45   It's very important to look in iTunes Connect and to look at the, or rather I guess now it's in the organizer in Xcode,

00:21:54   to look at the crash report that Apple's getting for you.

00:21:57   Look at what's being gathered there.

00:22:00   There's a whole lot of different problems that your app can have in the wild.

00:22:04   And the fact that we can get our crash reports basically all the time from Apple,

00:22:10   that whenever an app crashes we get notified about that, that's pretty awesome.

00:22:14   There's of course also lots of different other services as well,

00:22:17   but there are some types of crashes that only Apple's crash logs will generally, reliably be able to report to you.

00:22:24   That includes things like if iOS kills your app for some reason that isn't necessarily a crash,

00:22:30   but if your app is getting killed in the background or something, or if your extensions are getting killed,

00:22:34   then that's things that iOS can provide very useful feedback to if you look for it.

00:22:40   And that's all in the Xcode organizer.

00:22:42   And one of the things I can recommend highly is look at those,

00:22:46   and a lot of times the organizer won't tell you, like if you get like,

00:22:49   I was just spending a lot of time debugging crashes in my today widget, or my widget I guess,

00:22:54   and if you look at just the crash log it's not very helpful in the organizer.

00:23:00   But if you right click and show in finder on that crash and you dive into the bundle

00:23:05   and you can get the raw crash logs that are inside of that bundle,

00:23:08   when you look at the raw logs you can get a termination code.

00:23:11   And a lot of times that is springboard telling you something.

00:23:14   And there's a great technical note, TN-2151, Understanding and Analyzing Application Crash Reports.

00:23:21   And this tells you all the crazy codes that your app might be killed with.

00:23:25   So one of the things that I was facing is my extension was accessing the SQLite database

00:23:32   in the shared app group container.

00:23:34   I don't recommend doing this now, but now I know better.

00:23:37   But if you're accessing an SQLite database in a shared container in an extension,

00:23:42   at the time that the OS suspends you, it will kill your app with a certain code that says deadlock in hex.

00:23:48   And if you didn't look at this document, and if you didn't find that crash report and find that code,

00:23:54   and if you didn't then look at this document to see what that means,

00:23:57   and it tells you right here, it's been terminated by the OS because it held onto a file lock

00:24:01   or SQLite database during suspension.

00:24:03   And before I found all this out, I just didn't know why my extension just kept crashing for everybody.

00:24:11   And people kept reporting things like, "Hey, your app is crashing in the background for some reason.

00:24:14   I'm getting all these logs on my phone. What's going on?"

00:24:16   iOS tells you these things. It's telling you useful information here.

00:24:19   And so if you look at those crash logs in the Xcode organizer,

00:24:24   and if you look up what these different termination codes mean, you'll probably find out something useful.

00:24:30   And this is all like, and we have wonderful things like,

00:24:34   if you're kind of wondering whether you fixed a rare crash or not,

00:24:38   you can look at the analytics section of iTunes Connect, and it will graph for you

00:24:42   how many crashes per day your app is having.

00:24:45   And it marks on the graph where your updates occur.

00:24:49   So you can say, "Oh look, when this update was released, the crashes dropped by half.

00:24:55   So the thing I think I fixed, I probably did fix."

00:25:00   There's all sorts of useful things you can get from crash logs and things like that.

00:25:04   And one of the reasons why I do also, as I said earlier, kind of recommend against unnecessary use of dispatch async

00:25:12   is because that makes your crash logs less useful to you.

00:25:15   Because usually the crash logs will not report where the block was dispatched from.

00:25:22   Some of the more advanced ones, like hockey, try to do this, and they have mixed degrees of success in my experience.

00:25:28   And so if your code is simpler, then any crash log you get will also be more useful to you.

00:25:35   Yeah, and I think crash logs are definitely a great source for so much of this stuff.

00:25:41   And I feel like, in general, I will say that Xcode, as much as I don't use all of its tools,

00:25:46   some of the stuff that you're talking about, of going into the organizer, there's some really useful tools that we have at our disposal

00:25:56   to be tracking a lot of this stuff down now, which I don't think we had before.

00:25:59   It makes me think even of one of the other tools that I use a lot in debugging, in a strange way, is version control.

00:26:07   And specifically, get blame is often incredibly helpful in trying to track down weird situations in my apps,

00:26:18   where code is doing something weird, and I'm trying to work out why I structured it in the way I structured it.

00:26:23   And this is something that I love in Xcode, where you can go into the top right, where you go into the source control thing,

00:26:29   and you can do blame, and it will show you all of, for every line of code in your app,

00:26:35   it will show you which commit that line of code was introduced into your app with.

00:26:39   And so you often will end up happening there. By using that view, I can see, "Huh, that's weird.

00:26:45   Here's this one random line of code that is left over from an old version, that everything else was updated."

00:26:51   Or situations like that, and Xcode is really, and then you can, if you double click on it, it will show you that commit,

00:26:58   and it will show you the relevant, how that file changed with that commit.

00:27:04   And that's another example of one of these little tools in Xcode where it's like, it helps you understand what's going on,

00:27:10   in the same way that, yeah, that's a really great one.

00:27:13   I mean, I love in the crash logs where it can help you, take you right to where the crash happened,

00:27:18   rather than sitting there and trying to work out which, you know, if this crash happened in this file on this line number,

00:27:27   like, well, that's useful, but it's also way more useful to just take me to that line number.

00:27:32   So there's definitely some great tools in Xcode that I think make a lot of debugging a lot simpler, and that I'm very grateful for.

00:27:38   I would also say, when you're running your app and you're trying to figure out maybe what's going on with the bug,

00:27:43   always watch that pane in Xcode that shows you the running CPU usage and memory of the app.

00:27:50   And of course, you can go into instruments and get, you know, like a more fine-grained version of this.

00:27:53   But like, yesterday, there was this really tricky bug that I could not figure out.

00:27:57   Like, the app just started behaving really weirdly when loading episodes of a certain podcast, and it, like, really weirdly.

00:28:03   And it would eventually disconnect from Xcode. It was a massive, massive weird bug.

00:28:07   And Springboard would crash.

00:28:09   And what led me to the solution was that I was, I happened to be glancing at that pane that was showing me the resource graphs in Xcode during one of the runs,

00:28:18   and I noticed that right before it crashed, memory usage skyrocketed all the way up to, like, a gig, and then everything freaked out and blew up.

00:28:26   And so then I was able to say, "Hey, wait a minute. That's unusual. That stands out.

00:28:31   Let me run it under the allocations instrument run that can show you where all your memory is being used."

00:28:38   And I did that and figured out what the bug was within, like, a half hour after that.

00:28:44   This is a bug that's been bothering me for weeks. I couldn't figure it out.

00:28:47   And then within a half hour of noticing, "Hey, this thing is out of the ordinary on the memory meter here," I had to fix.

00:28:52   So, like, you know, use the tools that are available to you when appropriate, or just use nslog and printf debugging everywhere.

00:29:01   Yeah. And, I mean, the love of what you hit on there, though, is one thing that I will say that I love about debugging is that so often you get to the end,

00:29:08   and there's, like, a relatively straightforward solution that fixes the problem, and, like, that is one of the greatest joys of being a software development.

00:29:16   When you, like, you see the problem, you see the solution, and you can implement it, and, like, it's gone.

00:29:22   And this thing that was problematic and hurting your users is now just completely vanished.

00:29:27   And, like, that's, like, a great victory, and I always love when that happens.

00:29:30   It doesn't always happen, but when it does, it's absolutely awesome. So it's the one reason I love debugging.

00:29:35   That's why we do all this, right? That feeling when it finally works. We're like, "Yes! I love that feeling."

00:29:40   Yes.

00:29:41   All right. Thanks, everybody, for listening this week, and we'll talk to you next week.

00:29:44   Bye.

00:29:45   [