Archive for May 10, 2018

Thursday, May 10, 2018

The Laws of Core Data

Dave DeLong (tweet):

In my conversations with developers, I’ve heard a pretty common theme from them that “Core Data is hard” or “Core Data is buggy” or “I could never get it to work right and gave up on it”.

I’ve spent a lot of time using Core Data and thought I’d share my “Laws of Core Data”. These are a set of rules I’ve developed over time on how to use Core Data in such a way that it is almost entirely painless. When I follow these rules, I almost never have any problems using it.

Of particular note is that, contra Zarra, he thinks child contexts are usually unnecessary.

Colin Cornaby:

I find that when most people complain that CoreData is not thread safe out of the box, they’re really saying “I’m able to ignore that my existing model code is also not thread safe.”

Core Data has its issues—for example, there is a lot to learn, it is verbose, there are some persistent bugs, and in some cases it’s much slower than using SQLite directly—but overall I think it’s unfairly maligned.

Update (2018-05-11): Marcus Zarra:

All of the internal workings of your Core Data stack, your persistence and data model absolutely should be stored in the persistent container and that container should be injected through your controllers in your application.

[…]

An NSManagedObject is an NSObject that has additional functionality added. It should absolutely be treated as if it is an NSObject because it is an NSObject. NSManagedObject instances are your data objects and should be treated as such. Creating Plain Old Objects on top of NSManagedObject instances is asking for pain and data corruption.

The crux of the issue is that NSManagedObject is an NSObject that doesn’t obey that class’s standard contract. So it should not be treated as such. That said, I’m not sure there would be anything to gain by actually making it a separate root class. Creating plain objects on top of NSManagedObject instances can be a useful pattern. It has really simplified some of my code and made it faster and more robust. But I would only do this on a case-by-case basis, not as a regular part of using Core Data.

If your app talks to anything else then you will want to use parent-child contexts.

The parent context should be on the user interface thread (the main thread) and when you need to do asynchronous work with your data objects (importing or exporting) then you want to do that on another thread and that work should be done in a child context.

A child context simplifies the transfer of notifications of data changes and greatly simplifies using Core Data in a multi-threaded environment.

See also: Colin Cornaby and Marcel Weiher.

Update (2018-05-15): See also: Peter Steinberger.

C Is Not a Low-level Language

David Chisnall (Hacker News):

In the wake of the recent Meltdown and Spectre vulnerabilities, it's worth spending some time looking at root causes. Both of these vulnerabilities involved processors speculatively executing instructions past some kind of access check and allowing the attacker to observe the results via a side channel. The features that led to these vulnerabilities, along with several others, were added to let C programmers continue to believe they were programming in a low-level language, when this hasn't been the case for decades.

[…]

A modern Intel processor has up to 180 instructions in flight at a time (in stark contrast to a sequential C abstract machine, which expects each operation to complete before the next one begins). A typical heuristic for C code is that there is a branch, on average, every seven instructions. If you wish to keep such a pipeline full from a single thread, then you must guess the targets of the next 25 branches.

[…]

Consider another core part of the C abstract machine's memory model: flat memory. This hasn't been true for more than two decades. A modern processor often has three levels of cache in between registers and main memory, which attempt to hide latency.

[…]

A processor designed purely for speed, not for a compromise between speed and C support, would likely support large numbers of threads, have wide vector units, and have a much simpler memory model. Running C code on such a system would be problematic, so, given the large amount of legacy C code in the world, it would not likely be a commercial success.

Previously: Intel CPU Design Flaw Necessitates Kernel Page Table Isolation.

Update (2018-07-04): See also: Lambda the Ultimate.

“Black Dot” Unicode Bug

Benjamin Mayo:

A new Unicode text bug is being spread around today, popularised by a video by EverythingApplePro. It’s being called the ‘black dot’ bug because of its origins on Android as a bug relating to WhatsApp: it was being spread with the following emoji: <⚫>👈🏻. The iOS version of this bug is a bit different in its mechanics, but neither variants actually rely on the visible black dot character to cause the freezes and crashes.

The secret is that the strings contain thousands of hidden invisible Unicode characters, which churns through CPU cycles as the system attempts to process them. If this specially crafted text is sent through Messages, it will result in repeated crashes when the recipient tries to read it.

ctxppc:

I’ve already said it but Apple should revisit the text rendering architecture. Unicode is so complex it cannot be trusted in the same process as the app (or SpringBoard). Rendering should be done off-process just like how the window server on macOS deals with windows, Mission Control, and the mouse cursor (which continue to work even when an app freezes).

Once a particular string hangs or crashes the rendering process, it should be blacklisted and dealt appropriately until an update comes around which fixes the issue. The process could even report the blacklisted string to Apple (with permission from the user) so that it could be fixed early on.

Various Web rendering and font tasks are also handled out-of-process already.

Previously: Another iOS Crash Caused By Sending Unicode Character.

iOS 11’s Streamlined, Yet Extensible File Management

Federico Viticci:

Notably, while some Mac users may scoff at my delight in iOS 11’s document browser and third-party storage locations, I think it’s remarkable how what I described above can be accomplished just by installing apps and extending the system without questionable installers or system modifications. Yes, I could probably save time by performing the same actions with a shell script on macOS, but I prefer having an intuitive GUI and a file manager that can be extended just by downloading new apps from the App Store. While not perfect, the document browser and file provider extensions are some of the most exciting changes in iOS 11 with a lot of untapped potential because they are deeply integrated with the system and consistent with iCloud Drive.

Which is why I’d like Apple to improve some aspects of this workflow: it’d be great, for instance, if Working Copy could show colored indicators for documents that haven’t been committed in the document browser, or perhaps offer a button to refresh the contents of a repository and update its contents inline within the Files view. Similar features are available in the Mac’s Finder; Apple should continue to borrow from it as they work on what’s next for Files.

JPEG Decoding With the Best

Luke Parham (via Indie iOS Focus Weekly):

That’s right, any time you have a UIImage created from a JPEG, assigning it to be displayed by an image view means that an implicit CATransaction will be created and your image will be copied, decompressed, and rendered on the next display pass triggered by the run loop. […] This all happens on the main thread.

[…]

Well you can’t make UIImageView do its decoding more quickly, but if you’re so inclined, you are able to preemptively render the jpeg yourself!

[…]

First, there is, unfortunately, no API that’s been exposed for you to tell whether a given UIImage has already been decoded or not. This means that if you’re using this trick, you’ll want to make sure to carefully cache and re-use these images when you can since they take up a lot more memory than their compressed counterparts.

Second, this particular method is technically "offscreen rendering" in that it’s no longer hardware accelerated, but it’s the ok-when-it’s-useful kind of offscreen rendering, not the kind that makes your GPU stall.