Irbisa · Cheatsheet
Objective-C Runtime & Interop
Message dispatch, swizzling, bridging, KVO internals, when Objective-C is still needed
What is the Objective-C runtime, and what does it mean that Objective-C sends messages instead of calling methods?
What happens when you send a message to
nilin Objective-C, and how does Swift handle the same situation?What is the difference between
@objc,@objcMembersanddynamic, and why is@objcalone not enough for KVO or swizzling?final class Player: NSObject { @objc var a = 0 // Objective-C sees it; Swift callers bypass the runtime @objc dynamic var b = 0 // every call is a message send: KVO and swizzling work var c = 0 // Swift only}…What are KVC and KVO, how do you observe a property from Swift with
observe(_:options:changeHandler:), and what replaces KVO in new Swift code?final class Order: NSObject { @objc dynamic var status = "new" // @objc for KVC, dynamic for KVO} // KVC: read and write by string key at run timelet order = Order()…How does an Objective-C category differ from a class extension, and what happens when two categories add a method with the same name?
What is the difference between
+loadand+initialize, and why can a Swift class not override either?@implementation Analytics// Called by the runtime for every class and category that implements it,// when the image is loaded, before main(). Not inherited. Costs launch time.+ (void)load { // keep it tiny: no other classes may be ready yet}…What do the Objective-C property attributes
atomic,nonatomic,strong,weak,copyandassignmean, and doesatomicmake a property thread-safe?@protocol ProfileDelegate; @interface Profile : NSObject@property (nonatomic, copy) NSString *name; // stores a copy: a mutable string passed in cannot change later@property (nonatomic, strong) NSArray<NSString *> *tags; // +1, the default for objects@property (nonatomic, weak) id<ProfileDelegate> delegate; // no +1, becomes nil when the delegate is freed…How do you attach a stored property to a class you do not own, and what do associated objects cost?
// The key only needs a unique, stable address; its value is never read.private nonisolated(unsafe) var badgeTextKey: UInt8 = 0 extension UIView { var badgeText: String? { get { objc_getAssociatedObject(self, &badgeTextKey) as? String }…In an Objective-C block, why do you write the
weakSelf/strongSelfdance, and what does__blockchange under ARC?- (void)reload { __weak typeof(self) weakSelf = self; // the block must not own self [self.client loadWithCompletion:^(NSArray<NSString *> *items) { __strong typeof(weakSelf) strongSelf = weakSelf; // pin it for the whole block if (!strongSelf) { return; } // screen already closed strongSelf.items = items;…How do you make an Objective-C header read well from Swift: nullability,
NS_SWIFT_NAME,NS_REFINED_FOR_SWIFTand friends?// PaymentService.hNS_ASSUME_NONNULL_BEGIN typedef NS_CLOSED_ENUM(NSInteger, PMTStatus) { // a frozen Swift enum PMTStatusPending, PMTStatusPaid,…What happens when an object receives a message it has no method for, and how can you step into that process?
// Intercepts one delegate callback and forwards everything else untouchedfinal class ScrollDelegateProxy: NSObject, UIScrollViewDelegate { // read by the nonisolated NSObject overrides below; UIKit uses it on the main thread nonisolated(unsafe) weak var original: UIScrollViewDelegate? var onScroll: ((CGPoint) -> Void)?…What is method swizzling, how do you do it safely, and why is it risky in production?
/// Swizzles `original` on `cls` without touching its superclass. Call it once.func swizzle(_ cls: AnyClass, _ original: Selector, with replacement: Selector) { guard let originalMethod = class_getInstanceMethod(cls, original), let replacementMethod = class_getInstanceMethod(cls, replacement) else { return }…What is
isa, and how doesobjc_msgSenduse it, the method cache and the superclass chain to find an implementation?What does this code print, and what does KVO do to the object that explains the output?
class Upload: NSObject { @objc dynamic var progress = 0.0 @objc var status = "idle"} let upload = Upload()let a = upload.observe(\.progress) { u, _ in print("progress", u.progress) }let b = upload.observe(\.status) { u, _ in print("status", u.status) } upload.progress = 0.5upload.status = "running"upload.setValue("done", forKey: "status")Find the bug: screen analytics work until an iPad user opens a second window, then stop for every screen. What is wrong with this swizzle?
extension UIViewController { static func enableScreenTracking() { let original = class_getInstanceMethod(UIViewController.self, #selector(viewDidAppear(_:)))! let tracked = class_getInstanceMethod(UIViewController.self, #selector(tracked_viewDidAppear(_:)))! method_exchangeImplementations(original, tracked) } @objc dynamic func tracked_viewDidAppear(_ animated: Bool) { tracked_viewDidAppear(animated) // runs the original viewDidAppear Analytics.screen(String(describing: type(of: self))) }}final class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { UIViewController.enableScreenTracking() }}What is toll-free bridging, and how do ARC bridge casts and
Unmanageddecide who releases a Core Foundation object?// Toll-free: NSString and CFString are one object, the cast is freelet ns: NSString = "Café"let length = CFStringGetLength(ns as CFString) // 4 // Annotated CF API: Swift's ARC releases the result for youlet uuid = CFUUIDCreateString(nil, CFUUIDCreate(nil))! as String…What does bridging between
StringandNSStringcost, and why does anNSRangebuilt fromtext.countmiss matches?let text = "Cafe\u{301} \u{1F44D}\u{1F3FD} rocks"print(text.count, text.utf16.count) // 12 16 let regex = try! NSRegularExpression(pattern: "rocks") // wrong: Character count is not a UTF-16 length, the range stops short…In 2026, when do you still need Objective-C in an iOS codebase, and how would you migrate a large Objective-C module to Swift?