Last active
January 1, 2024 04:09
-
-
Save saagarjha/7f0ae20e472c2ecfe9521782ce3649f2 to your computer and use it in GitHub Desktop.
Fix an Xcode hang caused by FB11645580 due to IDERunDestination registering thousands of duplicate KVO observers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://gist.github.com/saagarjha/ed701e3369639410b5d5303612964557 | |
#import "swizzler.h" | |
#import <Foundation/Foundation.h> | |
#import <cstddef> | |
#import <cstdlib> | |
#import <dlfcn.h> | |
#import <mach-o/dyld.h> | |
#import <mutex> | |
#import <string> | |
#import <tuple> | |
#import <unordered_map> | |
struct TupleHasher { | |
std::size_t operator()(const std::tuple<NSObject *, NSString *, NSUInteger, id> &tuple) const { | |
// This sucks but it's good enough | |
return std::get<0>(tuple).hash ^ std::get<1>(tuple).hash ^ std::get<2>(tuple) ^ reinterpret_cast<uintptr_t>(std::get<3>(tuple)); | |
} | |
}; | |
static auto loader = []() { | |
std::string buffer; | |
uint32_t size = 0; | |
_NSGetExecutablePath(buffer.data(), &size); | |
buffer = std::string(size, 0); | |
_NSGetExecutablePath(buffer.data(), &size); | |
auto url = [NSURL fileURLWithPath:[NSString stringWithCString:buffer.data() encoding:NSASCIIStringEncoding]]; | |
auto path = [[[url.pathComponents subarrayWithRange:NSMakeRange(0, 4)] componentsJoinedByString:@"/"] stringByAppendingString:@"/SharedFrameworks/DVTFoundation.framework/Versions/A/DVTFoundation"]; | |
dlopen(path.UTF8String, RTLD_LAZY); | |
unsetenv("DYLD_INSERT_LIBRARIES"); | |
return 0; | |
}(); | |
static Swizzler<id, NSObject *, NSString *, NSUInteger, id> NSObject_dvt_newObserverForKeyPath_options_withHandlerBlock_ { | |
NSObject.class, @selector(dvt_newObserverForKeyPath:options:withHandlerBlock:), [](auto self, auto keyPath, auto options, auto handlerBlock) { | |
if ([keyPath isEqualToString:@"name"]) { | |
static std::mutex mutex; | |
static std::unordered_map<std::tuple<NSObject *, NSString *, NSUInteger, id>, id, TupleHasher> observerCache; | |
auto tuple = std::make_tuple(self, keyPath, options, handlerBlock); | |
std::lock_guard<std::mutex> lock(mutex); | |
if (auto location = observerCache.find(tuple); location != observerCache.end()) { | |
[location->second retain]; | |
return location->second; | |
} else { | |
auto result = NSObject_dvt_newObserverForKeyPath_options_withHandlerBlock_(self, keyPath, options, handlerBlock); | |
observerCache[tuple] = result; | |
// Intentional over-retain, to prevent this token from ever being deallocated | |
[result retain]; | |
return result; | |
} | |
} else { | |
return NSObject_dvt_newObserverForKeyPath_options_withHandlerBlock_(self, keyPath, options, handlerBlock); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment