I once took over an app that did a live search of a dictionary as the user typed. Looking over the code, I noticed that the memory management was a little funky, so I did a leak analysis on it. Turned out it was leaking something like 3,000 objects with every single keystroke. It still "worked," because the OS acts as the ultimate GC by cleaning up all of an app's memory when it terminates. Users mostly didn't leave it running long enough for the objects to build up, and if they did then it would die on its own eventually.
There's a famous story of Microsoft's heroic efforts to maintain backwards compatibility in Windows. The original version of SimCity would free memory and then read from it. This worked fine on Windows 3.x because the newly freed memory would be left undisturbed until something else allocated new memory. Windows 95 didn't work that way, so SimCity started crashing. Microsoft added code to check if SimCity was running and change the behavior of the memory allocator to stop it from crashing.
I've been programming on Apple platforms for a long time. The current standard languages are Objective-C and Swift, both with automatic reference counting (which is mostly a GC, although you have to manually break cycles). Automatic reference counting is a relatively new addition to Objective-C, and before 2011, everyone did manual reference counting instead. For a long time, the state of the art was to follow the official rules for writing refcounting code and occasionally look for leaks and crashes by running memory debugging tools on the live app. About eight years ago, the Clang project put out their static analyzer which could look at Objective-C code and tell you when you weren't following the rules properly. The reaction of every Objective-C programmer I knew, even the really good ones, upon running the static analyzer was something like, "holy shit, I had no idea my code was so full of bugs!"