From 671d71b70c20feb37ecd00d696ca6c1b9ddf7c44 Mon Sep 17 00:00:00 2001 From: Eliot Lash Date: Thu, 29 May 2025 14:20:47 -0700 Subject: [PATCH 1/4] Ignore .swiftpm folder which can be added when this repo is used as a submodule during development Signed-off-by: Eliot Lash --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3552f2b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.swiftpm From 08d32967e547ef117024cafaf218893be96d5eec Mon Sep 17 00:00:00 2001 From: Eliot Lash Date: Thu, 29 May 2025 14:20:48 -0700 Subject: [PATCH 2/4] Add public headers to Package.swift publicHeadersPath to expose them to other packages which import this package. Signed-off-by: Eliot Lash --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index 7731bc7..397097f 100644 --- a/Package.swift +++ b/Package.swift @@ -22,6 +22,7 @@ let package = Package( "swift_shims", ], path: "Sources/FNZombie", + publicHeadersPath: "include", cSettings: [ .headerSearchPath("include"), .unsafeFlags(["-fno-objc-arc"]) From 114d231df2d82880ee4d1e88f87af945438e5e3f Mon Sep 17 00:00:00 2001 From: Eliot Lash Date: Thu, 29 May 2025 14:20:48 -0700 Subject: [PATCH 3/4] Implement methodForSelector: in FNZombie which can provide more useful diagnostics in cases where this runtime method is first called on the zombie before other methods may be called. Signed-off-by: Eliot Lash --- Sources/FNZombie/FNZombie.h | 2 ++ Sources/FNZombie/FNZombieService.m | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/Sources/FNZombie/FNZombie.h b/Sources/FNZombie/FNZombie.h index 7979ecf..fa869b1 100644 --- a/Sources/FNZombie/FNZombie.h +++ b/Sources/FNZombie/FNZombie.h @@ -27,6 +27,8 @@ __attribute__((objc_root_class)) withObject:(id)anArgument afterDelay:(NSTimeInterval)delay; +- (IMP) methodForSelector:(SEL) aSelector; + @end #endif /* FNZombie_h */ diff --git a/Sources/FNZombie/FNZombieService.m b/Sources/FNZombie/FNZombieService.m index fa61346..79a99fc 100644 --- a/Sources/FNZombie/FNZombieService.m +++ b/Sources/FNZombie/FNZombieService.m @@ -321,4 +321,12 @@ - (void)performSelector:(SEL)aSelector ]; }; +- (IMP) methodForSelector:(SEL) aSelector { + [[FNZombieService sharedInstance] + crashWithObject:self + withSelector:aSelector + fromSelector:_cmd + ]; +} + @end From 135fcb5fd0c1a49d714a5e0a6e643d6bcb13a2b3 Mon Sep 17 00:00:00 2001 From: Eliot Lash Date: Thu, 29 May 2025 14:20:48 -0700 Subject: [PATCH 4/4] add basic info on this package to README Signed-off-by: Eliot Lash --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index 8f4a2fd..aa63294 100644 --- a/README.md +++ b/README.md @@ -1 +1,46 @@ # FNZombie + +Custom implementation of zombie objects for debugging bad access crashes. This library is an alternative, more customizable implementation of Apple's built-in `NSZombie`. + +Please see: +* https://funcorp.dev/blog/stopping_nszombie_invasion +* https://developer.apple.com/documentation/xcode/investigating-memory-access-crashes +* https://developer.apple.com/documentation/xcode/investigating-crashes-for-zombie-objects + +How to call from a Swift app: + +Import the SwiftPM package: +```swift +let package = Package( + // ... + dependencies: [ + .package(url: "https://github.com/funcorp/FNZombie.git", .branch("master")) + ], + targets: [ + .target( + name: "", + dependencies: [ + .product(name: "FNZombie", package: "FNZombie") + ] + ) + ] +) +``` + +Import the package somewhere early on in your init and initialize the zombie service, ex: + +```swift +import FNZombie + +@main +class MyAppDelegate: iOSHostApplication { + + override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + FNZombieService.sharedInstance().enable(withBufferSize: 128) // This buffer size can be whatever you want + + } +} +``` + +Now, run your app and check the logs for any zombie object calls when the app crashes. \ No newline at end of file