Irbisa · Шпаргалка
Objective-C runtime и интероп
Отправка сообщений, swizzling, бриджинг, KVO изнутри, когда ObjC всё ещё нужен
Что такое Objective-C runtime и что значит, что Objective-C отправляет сообщения, а не вызывает методы?
Что произойдёт, если в Objective-C отправить сообщение
nil, и как Swift ведёт себя в той же ситуации?Чем отличаются
@objc,@objcMembersиdynamic, и почему одного@objcне хватает для KVO или 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}…Что такое KVC и KVO, как наблюдать за свойством из Swift через
observe(_:options:changeHandler:)и что заменяет KVO в новом Swift-коде?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()…Чем категория в Objective-C отличается от class extension, и что будет, если две категории добавят метод с одинаковым именем?
Чем
+loadотличается от+initialize, и почему Swift-класс не может переопределить ни один из них?@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}…Что означают атрибуты свойств Objective-C
atomic,nonatomic,strong,weak,copyиassign, и делает лиatomicсвойство потокобезопасным?@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…Как добавить хранимое свойство классу, который вам не принадлежит, и чего стоят associated objects?
// 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 }…Зачем в блоке Objective-C писать связку
weakSelf/strongSelf, и что меняет__blockпод 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;…Как сделать заголовок Objective-C удобным для Swift: nullability,
NS_SWIFT_NAME,NS_REFINED_FOR_SWIFTи другие макросы?// PaymentService.hNS_ASSUME_NONNULL_BEGIN typedef NS_CLOSED_ENUM(NSInteger, PMTStatus) { // a frozen Swift enum PMTStatusPending, PMTStatusPaid,…Что происходит, когда объект получает сообщение, для которого у него нет метода, и как можно вмешаться в этот процесс?
// 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)?…Что такое method swizzling, как делать его безопасно и чем он опасен в продакшене?
/// 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 }…Что такое
isa, и какobjc_msgSendс его помощью, через кеш методов и цепочку суперклассов находит реализацию?Что выведет этот код, и что KVO делает с объектом, из-за чего вывод именно такой?
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")Найдите баг: аналитика экранов работает, пока пользователь iPad не откроет второе окно, а потом пропадает для всех экранов. Что не так с этим swizzling?
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() }}Что такое toll-free bridging, и как bridge-приведения ARC и
Unmanagedопределяют, кто освобождает объект Core Foundation?// 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…Чего стоит бриджинг между
StringиNSString, и почемуNSRange, построенный изtext.count, пропускает совпадения?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…Когда в 2026 году в iOS-проекте ещё нужен Objective-C, и как бы вы перевели большой Objective-C-модуль на Swift?