My Approach to Learning iOS
This post will discuss my approach to learning about a vast landscape of content in the iOS world, delve into some experience I had recently on a mock technical interview, and wrap up with some questions to ponder at the end. For any interested readers, my background in iOS started late 2014/early 2015 as a hobby relegated to after-work hours and weekends and I am now 11 weeks into a 12 week immersive bootcamp (Flatiron School)
I’ve been preparing for the technical interview for awhile now — more so in the past week but I always knew it was on the horizon since I decided to pursue a career in iOS development. The tech interview is a capstone moment where you’re first put to the test to see how well you really know your stuff; a crystallization of all of your coding knowledge to date. To me it’s a chance to talk to someone smarter than myself about something I’m really passionate about. And maybe get a job out of it? Seems alright to me.
Recently I had an opportunity to have a mock technical interview with a generous gentleman (and an extremely nice/awesome dude). This gentleman also happens to have an inspiring dedication to open source so in that vein I’m hoping to contribute a little bit back to the community by sharing some of my experiences in preparing for and having the interview.
I’d first like to discuss some higher level learning and interview related topics and then get into the nitty-gritty which I’m sure you’re all here for.
I Don’t Know
One of the most important things to understand is exactly what you don’t understand. I’m going to repeat that because it’s super important. One of the most important things to understand is exactly what you don’t understand. Try to challenge yourself everyday by learning something new — especially when it comes to topics you know nothing about. Those are the low hanging fruit. I’ll even provide you a list of good topics later on to start with if you don’t already have a list.
In terms of applying this to an interview, a perfectly acceptable answer to an interview question is “I don’t know, but” followed up with a train of thought as to how you would go about finding the answer or a first potential solution. Compare this to “I don’t know.” Make your best educated guess — sometimes this guess will lead you down the right path to finding a viable answer. Interviewers are more interested in how you approach a problem and think through it rather than just having the right answer ready to go.
There may be times you just know absolutely nothing about the topic. This will probably happen at some point during a tech interview because they are supposed to be hard. My only advice there would be to not get flustered and still try to talk through it and think about it intelligently. This is where having a large breadth of knowledge can be helpful to draw upon.
Breadth vs. Depth
These are pretty loosely defined terms, but here’s how I think about them. Abstractly, I have an idea that there exists A Magical List (technical term) that has every single concept that an iOS developer could possibly know. When we start learning iOS we are handed this List but every item on it is hidden. Blank slate. It’s our job to discover those items and then learn them. Breadth would then refer to how many items are on your list and depth how well you know each item.
So how and in what order do we do expand our List? Do we try to exhaustively expand our List but not really know much about each individual item on it? That doesn’t seem very useful. What about being the preeminent expert in one or two items? That seems slightly better depending on what those items are, and certainly could be viable at some point in your career, but still feels somewhat lacking and would probably take a very long time. What’s really going on here is a fundamental concept in programming — tradeoffs. Get used to hearing that word because just about everything has a tradeoff.
My advice would be to consider that some concepts are more important than others at different points in your career to different employers. So if you’re just starting out, it’s probably not a good idea to spend a week and learn about how the Objective-C runtime works under the hood before you explore how to use an array and a dictionary in Objective-C (and/or Swift nowadays). Sure you could (and the Obj-C runtime is pretty interesting), but that is a huge tradeoff to make when balanced against data structures as those are critically important.
How to Start
I believe a good way to approach this problem is to always be switching context between two modes, depth (acquiring knowledge) and breadth (expanding your knowledge horizon). So let’s just pick one and start in depth mode.
For example, you absolutely need to understand how the basic building blocks of iOS apps work and how to use them to build an app. This would mean starting with Foundation and UIKit would be a great choice. In fact, this should be #1 and #2 on your list. Don’t start trying to understand #3, whatever that may be, and beyond until you can start a new project and get a working table view, scroll view, stack view, collection view, etc. up on screen and doing something useful in less than 10 minutes. At this point in your career you probably don’t need to understand the intricate details about how exactly everything works because you’re operating at a high level of abstraction. But you should have a healthy interest in that! It’s ok to not know but be aware you are at this high level of abstraction and slowly work towards understanding the why and how.
Once you get to a good point with those, it’s time to switch into breadth mode. As you were working on acquiring depth, you unknowingly started filling in items on your list and changing them from a “I didn’t know this thing even existed” state to a “I don’t know anything about this thing” state. Maybe you used delegates but you just copied code to get your table view to work. That is definitely something worth exploring. I’d start with those items that are fuzzy before moving on.
Filling In the Gaps
Once you’ve filled in most of the gaps you know you had, it’s time to start filling in the gaps for things you didn’t know you had. I’ll give an example of a path I went down recently. I was staring at an interface file one day and for some reason I was fixated on a list of a bunch of properties that were marked as (nonatomic, strong). I asked myself if I knew exactly what this code was doing.
So I started exploring all the different types of ways you can define a property. What are strong and weak references and why should I care? Why are all my class-based properties strong? Why are my primitives not strong?Why are all my delegates weak? I explored that topic and learned about retain cycles. This led into some discussion on ARC (automatic reference counting) which I had heard about before. I also remembered reading about the compiler now handling counting references for us as well as handling memory and deallocating objects. Cool, we got that for free — remember what I said about being at a high level of abstraction?
So if that’s happening automatically under the hood now then there must also have been apps made before ARC existed. How did they work? Did they have to manually count references and remove references? That doesn’t sound very friendly. Turns out it’s pretty close to what used to happen. What is a reference? This led into learning about memory management in general and why Objective-C primarily uses the heap vs. the stack. What’s the stack and what’s the heap? I learned about that too. Is it of specific use to me right now? Not yet but it might be someday and it certainly helped connect some dots.
So the theme here is practice a ton but don’t forget to take a step back and be curious and explore. Now let’s get into the actual topics I was asked to talk about and then some other things I’ve been exploring. I am just going to do a very cursory exploration of each of these topics. Consider these as topical talking points only and take the time to dig into each of them in greater detail.
My best advice on how to approach technical questions is to just start talking out loud and thinking through the problem. The below is the thought process I went through from start to finish. I’m sure the interviewer got tired of hearing me talk eventually but appreciated how I got from point A to point B.
Threading — main vs. background thread
- This question was masked as a tableview with bad scrolling performance.
- First stop, cellForRowAtIndexPath. Sure enough, an image is being called from disk every time the system asks for a cell. That’s an expensive operation.
- Instead of loading it on the main thread every time the tableView asks for a cell, we should move the loading to a background thread so that the main thread (which is where the UI drawing occurs) can proceed while that thread does some work and then update the UI once the loading in the background is done.
- My first solution to this was to move the image loading code into viewDidLoad, put each image into an array and access a pre-loaded image each time. Sounds great but that solution might not scale. What if you have 500 images? 10,000 images?
- Another solution could be to use background threads and keep the main thread free of image loading blockage. This was the right answer.
- This means we need to separate the code that loads the image and sets the image (this was done in one line) into two lines so it can go into 2 queues
- I ended up using a GCD dispatch_queue to solve this. I also could have used an NSOperationQueue or block.
Views — frames and bounds and lifecycle methods
- Next up, a view which loaded in portrait mode but when rotated to landscape stayed in portrait. This one seemed pretty simple but taking a step back, there are a lot of ways to solve this. Which one do we choose?
- Size classes? Auto layout constraints? Storyboard or code? UIViewController Rotation methods such as willRotateToInterfaceOrientation:duration: and such?
- I don’t really want to write constraints in code or in storyboard during a quick programming challenge — this probably isn’t the right approach and I suspect the interviewer is looking for something more practical.
- Is there a solution that might be faster than another?
- The problem was that the view was setting its frame once in viewDidLoad to the superview’s frame (portrait mode) and on rotation it was never reset. So how can we reset the frame each time it rotates?
- Sounds like a lifecycle method could be appropriate here.
- I chose to override viewWillLayoutSubviews and set the view’s frame to the bounds of its superview. This works because it’s called before the system actually lays out the view hierarchy and it gets called on rotation.
- There is likely a better solution but given the time constraints, it did the job
Dependencies and coupling — protocols, delegates, notifications
- Here is where things got interesting. I’m not going to go directly into what was discussed. Instead I’ll let Mr. Socrates take over from here.
- What is code coupling? What does it sound like?
- What’s a dependency? How might an object depend on another object?
- Can you have more than one dependency? Can you have no dependencies?
- What is the impact of depending on multiple objects? Does it mean something is tightly coupled?
- When can we use coupling advantageously? When is it disadvantageous?
- Can something be loosely coupled?
- What’s strong decoupling? (not a real question, more of an inside joke! but this does sort of make sense in a double negative kind of way)
- What design patterns does Objective-C use that would be considered tightly coupled? Loosely coupled?
- What is a one to one coupling/relationship? One to many?
- Where would you categorize delegates, protocols, and notifications within this framework of thinking?
- Bonus: What is dependency injection? Why might it be useful?
Learning List and Questions To Consider
Things that did not come up during a mock interview but I found interesting to think about in no particular order.
Describe Atomic vs. Nonatomic
- Which one is default behavior?
- Does using atomic mean you get thread-safety?
- Why would you choose to use one over the other?
- What does it mean to be thread-safe?
- What objects are thread-safe? Non thread-safe?
- What is ARC? Why does it exist?
- What’s it doing under the hood? How did it used to work before ARC?
- What is memory?
- How much memory does our app get to use?
- How much memory is on an iPhone 5, 6, 6s, 6+?
- How can we respond to memory warnings in our app?
- What’s a strong reference? Weak? How about copy, assign, retain?
- Which types of objects typically use each type in property declarations? Why?
- Why should you try to avoid retain cycles?
- How would you create a retain cycle?
- How would you break a retain cycle?
Blocks — when to use self, __weak self, __strong self
- __weak self — what does this line of code do?
- __strong self — how about this one?
- Why do we care about the above 2 lines of code?
- How do these answers help you understand retain cycles?
Views — rendering, layout and subview management
- What is a UIView responsible for?
- What is a frame? Bounds?
- What happens when a user touches the screen? How does the system decide what view handles a gesture? Can you override that behavior?
- What does is UIView’s superclass? How might this help you understand how the system determines which view receives touch events?
- What methods are called throughout a view’s lifecycle?
- Can you think of ways those lifecycle methods might be useful?
Other Important Lifecycles
- What states can an application be in?
- What methods are called before an application displays something on screen?
- How does the UIViewController lifecycle work? What methods are called? When are they called?
- How can we respond to these lifecycle events?
- What does this term mean?
- What role does NSObject play in all of this?
- What does it mean to be “Objective-C”? Can you describe what C is?
- How did Objective-C become Objective? What methods does the runtime implement to make messaging between objects happen?
- Were there any languages that were inspirations for Objective-C?
- Are there any interesting things we can do by understanding how Objective-C works under the hood?
Stack vs. Heap
- What is the stack? The heap?
- Why does Objective-C primarily allocate memory on the heap?
- Does it have anything to do with the way it uses pointers?
- What’s a pointer?
- How long does the stack exist? The heap?
- What implications does this have for memory management?
Explain how a UITableView and UITableViewController work together
- How does the interaction between these classes utilize the delegate pattern?
- How does it make use of protocols?
- Why did Apple make the choice to implement a method to dequeue a cell and reuse it? Might this have something to do with memory management?
- What kind of design decisions could you make to ensure smooth tableview scrolling?
How does Apple implement NSArray and NSDictionary and NSSet?
- I’m still learning this one! These are some questions I have.
- Is NSArray using a linked list somewhere? Does NSDictionary use a hash table?
- How does the data structure change with the size of the container? Does it even change?
- What performance issues should I be thinking about?
- Is there an optimal container for large sets of elements? Small sets? 100 items? 10,000 items? Does the type of object impact this question?
- How about when I plan to do a lot of custom querying?
- Is one faster to create than another?
- Which performs better for looking up individual elements?
- How do their mutable counterparts compare for all of the above?
Well that’s a little bit of what I’ve been thinking about lately. These questions were generally things I hadn’t had a chance to explore directly while working with Foundation and UIKit. Hope it helps frame some of your interview prep and iOS fundamentals. Remember that learning should never end. Write better code every day. Don’t forget to be nice and pay it forward.
Good luck out there!