The Per Rewrite Diary: Day 19
This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!
See the rest of the series here.
Whoopsie
So the plan today was to pause on writing code, and do what I think of as “catch-up” work. There are some inconsistencies among, for example, content view controllers — some have all kinds of view layout and initialization code in viewDidLoad()
, others dump that stuff into a child UIView
with setupView()
and setupConstraints()
methods. Pushing through on a daily basis with spike solutions and experiments makes for a lot of forward momentum, but it's important to take a step back and make sure you're tidying as you go — hence the list of 'papercuts,' or little issues that aren't a big deal on their own, but a real problem if they're left to accumulate.
Aside: Remember that this is a fairly small and simple app being built by a single person, so it really doesn't need a fancy and overcomplicated methodology. Experimenting and 'trying silly ideas' are not only allowed, they're encouraged.
So, that was the plan. But overnight I realized that something felt… unsettled in my brain. Every time I create a new form view, am I making sure the memory is being deallocated when it's dismissed?
I thought so — but you know that feeling. The one telling you that you've probably missed something.
So today I sat down and fired up Instruments, watching allocations as I navigate in and out of the add-product form. Sure enough, every time I present it, we get a ProductDetailContentViewController
being allocated, but not being de-allocated.
Well, crap.
Who's got two thumbs and didn't give a delegate a weak
reference? This guy. This, as you may know, creates a retain cycle, where the view controller can't be destroyed because it's got a strong reference to its delegate object. So, okay, add a weak
keyword and we're done, right?
🚫🚫🚫🚫🚫
Nope. Xcode refuses to compile the code and gives me the following error if I try that:
'weak' must not be applied to non-class-bound
'ProductDetailContentViewControllerDelegate';
consider adding a protocol conformance that has a class bound.
Oh. Okay… so what does that mean? I asked Frank about it, and here's what he explained:
“Make your protocol inherit from AnyObject. Essentially, the compiler is making sure your weak variable is a reference type and not a value type, because a weak value type doesn’t make sense.”
And yes, making the ProductDetailContentViewControllerDelegate
protocol conform to AnyObject
fixed the issue. This was a case of me looking for the issue somewhere that was just far enough removed from the actual problem, that I couldn't see the fairly obvious solution.
A thing I'm noticing: if you feel like you're fighting the language/compiler, asking questions like “why doesn't removeFromParent()
, y'know, remove from parent?” — step back. Re-evaluate and make sure you're asking the right question.
Oh yeah, and one final bit: why doesn't a weak value type make sense? Well, weak or strong is in relation to the reference one object has to another. You can't have a reference to a value type — you don't point to them, you copy them. You can only have a reference to the aptly-named reference type.
So, okay. Tomorrow, we're back to the papercuts.