惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
N
Netflix TechBlog - Medium
D
Docker
Stack Overflow Blog
Stack Overflow Blog
云风的 BLOG
云风的 BLOG
U
Unit 42
Recorded Future
Recorded Future
G
Google Developers Blog
T
Threatpost
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
The Blog of Author Tim Ferriss
C
Cyber Attacks, Cyber Crime and Cyber Security
A
Arctic Wolf
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
Project Zero
Project Zero
WordPress大学
WordPress大学
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
M
MIT News - Artificial intelligence
月光博客
月光博客
Spread Privacy
Spread Privacy
Know Your Adversary
Know Your Adversary
人人都是产品经理
人人都是产品经理
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Hacker News
The Hacker News
K
Kaspersky official blog
Simon Willison's Weblog
Simon Willison's Weblog
MongoDB | Blog
MongoDB | Blog
J
Java Code Geeks
I
Intezer
Y
Y Combinator Blog
P
Proofpoint News Feed
G
GRAHAM CLULEY
L
LINUX DO - 热门话题
Schneier on Security
Schneier on Security
B
Blog
Jina AI
Jina AI
V2EX - 技术
V2EX - 技术
雷峰网
雷峰网
Last Week in AI
Last Week in AI
V
V2EX
Martin Fowler
Martin Fowler
P
Palo Alto Networks Blog
Latest news
Latest news
Google DeepMind News
Google DeepMind News
L
Lohrmann on Cybersecurity
The Last Watchdog
The Last Watchdog

Peter Steinberger

OpenClaw, OpenAI and the future | Peter Steinberger Shipping at Inference-Speed | Peter Steinberger The Signature Flicker | Peter Steinberger Just Talk To It - the no-bs Way of Agentic Engineering | Peter Steinberger Claude Code Anonymous | Peter Steinberger Live Coding Session: Building Arena | Peter Steinberger My Current AI Dev Workflow | Peter Steinberger Essential Reading for Agentic Engineers - August 2025 | Peter Steinberger Just One More Prompt | Peter Steinberger Poltergeist: The Ghost That Keeps Your Builds Fresh | Peter Steinberger Don't read this Startup Slop | Peter Steinberger Essential Reading for Agentic Engineers - July 2025 | Peter Steinberger Self-Hosting AI Models After Claude's Usage Limits | Peter Steinberger Logging Privacy Shenanigans | Peter Steinberger VibeTunnel's first AI-anniversary | Peter Steinberger Making AppleScript Work in macOS CLI Tools: The Undocumented Parts | Peter Steinberger Peekaboo 2.0 – Free the CLI from its MCP shackles | Peter Steinberger Command your Claude Code Army, Reloaded | Peter Steinberger Essential Reading for Agentic Engineers | Peter Steinberger Slot Machines for Programmers: How Peter Builds Apps 20x Faster with AI | Peter Steinberger My AI Workflow for Understanding Any Codebase | Peter Steinberger stats.store: Privacy-First Sparkle Analytics | Peter Steinberger Showing Settings from macOS Menu Bar Items: A 5-Hour Journey | Peter Steinberger VibeTunnel: Turn Any Browser into Your Mac's Terminal | Peter Steinberger Vibe Meter 2.0: Calculating Claude Code Usage with Token Counting | Peter Steinberger llm.codes: Make Apple Docs AI-Readable | Peter Steinberger Automatic Observation Tracking in UIKit and AppKit: The Feature Apple Forgot to Mention | Peter Steinberger Peekaboo MCP – lightning-fast macOS screenshots for AI agents | Peter Steinberger Migrating 700+ Tests to Swift Testing: A Real-World Experience | Peter Steinberger Commanding Your Claude Code Army | Peter Steinberger Code Signing and Notarization: Sparkle and Tears | Peter Steinberger Vibe Meter: Monitor Your AI Costs | Peter Steinberger Claude Code is My Computer | Peter Steinberger Stop Over-thinking AI Subscriptions | Peter Steinberger Introducing Demark: HTML in. MD out. Blink-fast. | Peter Steinberger The Future of Vibe Coding: Building with AI, Live and Unfiltered | Peter Steinberger MCP Best Practices | Peter Steinberger Finding My Spark Again | Peter Steinberger Top-Level Menu Visibility in SwiftUI for macOS | Peter Steinberger Fixing keyboardShortcut in SwiftUI | Peter Steinberger Supporting Both Tap and Long Press on a Button in SwiftUI | Peter Steinberger On Using Apple Silicon Mac Mini for Continuous Integration | Peter Steinberger Apple Silicon M1: A Developer's Perspective | Peter Steinberger Gardening Your Twitter: Curating Your Timeline | Peter Steinberger Gardening Your Twitter: Growing Your Followers | Peter Steinberger Forbidden Controls in Catalyst: Optimize Interface for Mac | Peter Steinberger Disabling Keyboard Avoidance in SwiftUI's UIHostingController | Peter Steinberger The State of SwiftUI | Peter Steinberger Logging in Swift | Peter Steinberger Building with Swift Trunk Development Snapshots | Peter Steinberger Calling Super at Runtime in Swift | Peter Steinberger zld — A Faster Version of Apple's Linker | Peter Steinberger How to Fix LLDB: Couldn't IRGen Expression | Peter Steinberger Updating macOS on a Hackintosh | Peter Steinberger InterposeKit — Elegant Swizzling in Swift | Peter Steinberger The Great Mac Catalyst Text Input Crash Hunt | Peter Steinberger Jailbreaking for iOS Developers | Peter Steinberger Network Kernel Core Dump | Peter Steinberger How to macOS Core Dump | Peter Steinberger Kernel Panics and Surprise boot-args | Peter Steinberger The LG UltraFine 5K, kernel_task, and Me | Peter Steinberger Let's Try This Again | Peter Steinberger How We Work at PSPDFKit | Peter Steinberger Swizzling in Swift | Peter Steinberger WWDC for First-Timers, 2019 Edition | Peter Steinberger Challenges of Adopting Drag and Drop | Peter Steinberger Marzipan: Porting iOS Apps to the Mac | Peter Steinberger How to Use Slack and Not Go Crazy | Peter Steinberger Hardcore Debugging - Heavy Weapons for Hard Bugs | Peter Steinberger Binary Frameworks in Swift | Peter Steinberger Even Swiftier Objective-C | Peter Steinberger The Case for Deprecating UITableView | Peter Steinberger Running tests with Clang Address Sanitizer | Peter Steinberger UI testing on iOS, without busy waiting | Peter Steinberger Hiring a distributed team | Peter Steinberger Writing Good Bug Reports | Peter Steinberger Real-time collaboration, Apple, and you | Peter Steinberger Converting Xcode Test Runs to JUnit, the Fast Way | Peter Steinberger Efficient iOS Version Checking | Peter Steinberger Swifty Objective-C | Peter Steinberger Running UI Tests on iOS With Ludicrous Speed | Peter Steinberger A Pragmatic Approach to Cross-Platform | Peter Steinberger Surprises with Swift Extensions | Peter Steinberger Using ccache for Fun and Profit | Peter Steinberger UITableViewController designated initializer woes | Peter Steinberger Researching ResearchKit | Peter Steinberger The curious case of rotation with multiple windows on iOS 8 | Peter Steinberger UIKit Debug Mode | Peter Steinberger Retrofitting containsString: on iOS 7 | Peter Steinberger A Story About Swizzling "the Right Way™" and Touch Forwarding | Peter Steinberger Hacking with Aspects | Peter Steinberger Fixing UITextView On iOS 7 | Peter Steinberger Fixing What Apple Doesn't | Peter Steinberger How To Inspect The View Hierarchy Of Third-Party Apps | Peter Steinberger Fixing UISearchDisplayController On iOS 7 | Peter Steinberger Smart Proxy Delegation | Peter Steinberger Adding Keyboard Shortcuts To UIAlertView | Peter Steinberger How To Center Content Within UIScrollView | Peter Steinberger UIAppearance for Custom Views | Peter Steinberger Hacking Block Support Into UIMenuItem | Peter Steinberger
Investigating Thread Safety of UIImage | Peter Steinberger
Peter Steinberger · 2016-06-30 · via Peter Steinberger

UIImage is one of the most important classes in UIKit. When compared to browsers, image handling on iOS is still quite tricky. Loading even a medium-sized image on the main thread will likely take longer than 16.6 milliseconds. Naturally, people identify this as an issue and move image creation to background threads.

Generally, the code is based on the assumption that -[UIImage imageWithData:] and -[UIImage imageWithCGImage:] are thread safe. But are they? Since February 2015, there’s been an issue on the AFNetworking repository that involves crashes around these functions. Since we do a lot with probabilistic image pre-caching and decompression in PSPDFKit, we ran into the issue as well. The crashes are quite rare and eventually somebody proposed a workaround so they never were a major issue.

Let’s Not Simply Trust Rumors

There were rumors that this issue was going to be fixed in iOS 10. So after the WWDC dust settled, I became curious if this was indeed the case and began the investigation. Is it even safe to create an image on any thread? According to Apple’s Documentation, the answer is no or at the very least, it’s not guaranteed.

Because image objects are immutable, you cannot change their properties after creation. Most image properties are set automatically using metadata in the accompanying image file or image data. The immutable nature of image objects also means that they are safe to use from any thread.

(note the word use here - not create, although this is a bit vague)

In racking my brain, I did recall reading something about this in the iOS 4 release notes but when I went to find them, I discovered Apple’s documentation only goes back as far as iOS 5. Enter wayback machine to the rescue and you find the iOS 4 release notes.

In iOS 4, these were the UIKit Framework Enhancements made:

Drawing to a graphics context in UIKit is now thread-safe. Specifically: The routines used to access and manipulate the graphics context can now correctly handle contexts residing on different threads; String and image drawing is now thread-safe.; Using color and font objects in multiple threads is now safe to do.

Again, this statement only talks about drawing - not creating.

So, back to the original issue - how does several threads accessing the static builder cause a crash?

If we take a closer look at the crash stack trace we notice that it occurs during the deallocation of UITraitCollection with pointer being freed was not allocated printed out in the console. This indicates that the problem might be a race that causes a fresh UITraitCollection object to be over-released.

Crash

This bug must be fairly new. At some point, imageWithData: and imageWithCGImage: were meant to be thread safe, since the underlying CoreGraphics calls are all thread safe. But then, retina came, and deadlines, the code got more complex, suddenly images had a scale and an implicit trait environment (so they can swap out when the environment changes), and someone needed to create a UITraitCollection object.

So, I set a breakpoint on who creates this object:

(lldb) bt
* thread #2: tid = 0xcbd57, 0x0000000102264afe UIKit`-[UITraitCollection _initWithBuiltinTraitStorage:clientDefinedTraits:], queue = 'com.apple.root.default-qos', stop reason = breakpoint 2.1
  * frame #0: 0x0000000102264afe UIKit`-[UITraitCollection _initWithBuiltinTraitStorage:clientDefinedTraits:]
    frame #1: 0x0000000102267342 UIKit`+[UITraitCollection traitCollectionWithDisplayScale:] + 78
    frame #2: 0x00000001017433c3 UIKit`-[UIImage(UIImagePrivate) _initWithData:preserveScale:cache:] + 211
    frame #3: 0x000000010173e4e9 UIKit`+[UIImage imageWithData:] + 69
    frame #4: 0x00000001006e60f5 UIImageWithDataMultithreaded`__29-[ViewController viewDidLoad]_block_invoke((null)=0x00007fff5f517a28, idx=11) + 69 at ViewController.m:25

This object is definitely created on a background thread, which is wasteful. Why is this not cached? I also added my main thread guard that checks mostly for view access and that one did not trigger, so there are no views involved.

On iOS 10 it doesn’t seem to crash anymore - but it certainly did in iOS 9.3 - see my testcase. Let’s enable zombie objects to see what’s up here. It’s funny - the code here is so racy that the system sometimes even races on creating zombie classes:

ThreadSanitizer debugger support is active.
objc[47357]: Class _NSZombie_UIImage is implemented in both ?? and ??. One of the two will be used. Which one is undefined.
objc[47357]: Class _NSZombie_UIImage is implemented in both ?? and ??. One of the two will be used. Which one is undefined.

(This is no big deal, since it’s just for debugging, but definitely interesting)

As soon as we enable the thread sanitizer and/or zombies, it does not crash anymore. Disabling that and playing with it some more showed something interesting in the stack trace:

Backtrace

UITraitCollectionCacheForBuiltinStorage. The fun thing here is that this method lazily initializes a dictionary, using dispatch_once to make it thread safe, but then calls void CFDictionarySetValue ( CFMutableDictionaryRef theDict, const void *key, const void *value ); without any locking protecting it. Here value is the new trait collection that is created and then set. Now, we race in the dictionary setter and an object gets over-released and we crash in object_dispose.

In iOS 9.3, traitCollectionWithDisplayScale: simply calls through to traitCollectionWithDisplayScale.

How do things look in iOS 10?

The cache is gone - so things are more wasteful - BUT we no longer race since it seems the UITraitCollection objects do not share common data or access the main thread.

We can assume that the issue here has indeed be fixed. Apple clearly does not delete a cache for fun, especially not when there’s so much to do around UIKit. However since we were all a bit lazy here, there is no radar filed where I could cross-check it and Apple’s documentation seems inherently vague here.

A clear stand on UIImage creation thread safety would be great. I do believe that it’s highly beneficial to keep this thread safe and it seems it is the case. However, we’re a bit in the dark here and if it were to suddenly regress again, I’d love to know if this is, at least, a bug.

I filed rdar://problem/26954460 to enhance the documentation.

Additionally, we released a small open-source project with a workaround that patches UIKit at the correct place to fix the locking application-wide. The current fix for AFNetworking does not apply globally, since it only adds additional locking for the internal AFNetworking calls. As soon as somebody else invokes imageWithData: on a background thread we’re again facing a race condition. I’m not sure if it’s the best idea and admittedly the crash is probably quite rare. However, depending on the scale of your app, it might still save thousands of crashers each day.

If you enjoy going deep on problems, then PSPDFKit is the place for you to be. We’re hiring!