Post

Replies

Boosts

Views

Activity

Memory leak and a crash when swizzling NSURLRequest initialiser
When swizzling NSURLRequest initialiser and returning a mutable copy, the original instance does not get deallocated and eventually gets leaked and a crash follows after that. Here's the swizzling setup: static func swizzleInit() { let initSel = NSSelectorFromString("initWithURL:cachePolicy:timeoutInterval:") guard let initMethod = class_getInstanceMethod(NSClassFromString("NSURLRequest"), initSel) else { return } let origInitImp = method_getImplementation(initMethod) let block: @convention(block) (AnyObject, Any, NSURLRequest.CachePolicy, TimeInterval) -> NSURLRequest = { _self, url, policy, interval in typealias OrigInit = @convention(c) (AnyObject, Selector, Any, NSURLRequest.CachePolicy, TimeInterval) -> NSURLRequest let origFunc = unsafeBitCast(origInitImp, to: OrigInit.self) let request = origFunc(_self, initSel, url, policy, interval) return request.tagged() } let newImplementation = imp_implementationWithBlock(block as Any) method_setImplementation(initMethod, newImplementation) } // create a mutable copy if needed and add a header private func tagged() -> NSURLRequest { guard let mutableRequest = self as? NSMutableURLRequest ?? self.mutableCopy() as? NSMutableURLRequest else { return self } mutableRequest.setValue("test", forHTTPHeaderField: "test") return mutableRequest } Then, we have a few test cases: // memory leak and crash func testSwizzleNSURLRequestInit() { let request = NSURLRequest(url: URL(string: "https://example.com")!) XCTAssertEqual(request.value(forHTTPHeaderField: "test"), "test") } // no crash, as the request is mutable, so no copy is created func testSwizzleNSURLRequestInit2() { let request = URLRequest(url: URL(string: "https://example.com")!) XCTAssertEqual(request.value(forHTTPHeaderField: "test"), "test") } // no crash, as the request is mutable, so no copy is created func testSwizzleNSURLRequestInit3() { let request = NSMutableURLRequest(url: URL(string: "https://example.com")!) XCTAssertEqual(request.value(forHTTPHeaderField: "test"), "test") } // no crash, as the new instance does not get deallocated // when the test method completes (?) var request: NSURLRequest? func testSwizzleNSURLRequestInit4() { request = NSURLRequest(url: URL(string: "https://example.com")!) XCTAssertEqual(request?.value(forHTTPHeaderField: "test"), "test") } It appears a memory leak occurs only when any other instance except for the original one is being returned from the initialiser. Is there a workaround to prevent the leak, while allowing for modifications of all requests?
3
0
266
Oct ’24