I want to log some statements in deinit in each subclass of UIViewController in my project. I don't want to copy/paste the same lines in each view controller subclass.
Asked
Active
Viewed 1,497 times
3
-
1Are you talking about just custom view controllers you create or do you also need this for standard view controllers provided by the iOS SDK? – rmaddy Nov 17 '16 at 06:04
-
My current need is for custom view controllers but my concern is to check the swizzle availability for deint. – ZaEeM ZaFaR Nov 17 '16 at 06:39
-
Did you found any solutions? – Paul May 26 '17 at 03:17
-
Still not found. @Paul – ZaEeM ZaFaR Jun 06 '17 at 06:24
-
1Aaaahhhhh, we need this! – ullstrm Jun 15 '17 at 07:00
2 Answers
9
There is a way to achieve this.
You can't swizzle deinit
, but you can swizzle another method like viewDidLoad
to poison the class with associatedObject
. When viewController deallocates, the associatedObject gets deallocated as well.
final class Deallocator {
var closure: () -> Void
init(_ closure: @escaping () -> Void) {
self.closure = closure
}
deinit {
closure()
}
}
private var associatedObjectAddr = ""
extension UIViewController {
@objc fileprivate func swizzled_viewDidLoad() {
let deallocator = Deallocator { print("Deallocated") }
objc_setAssociatedObject(self, &associatedObjectAddr, deallocator, .OBJC_ASSOCIATION_RETAIN)
swizzled_viewDidLoad()
}
static let classInit: Void = {
let originalSelector = #selector(viewDidLoad)
let swizzledSelector = #selector(swizzled_viewDidLoad)
let forClass: AnyClass = UIViewController.self
let originalMethod = class_getInstanceMethod(forClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}()
}
Caveat
The closure will not get called exactly when viewController is deallocated, since the Deallocator
is deallocated after viewController is completely deallocated.

farzadshbfn
- 2,710
- 1
- 18
- 38
0
I updated the solution that indirectly calls swift code, might not be the best. You can get the de-init implementation by using NSSelectorFromString
and extend the implementation with swizzling and call swift code through bridging. Try this code that might help you:
//
// UIViewController+ExtendDealloc.h
// extension
//
// Created by Amir Kamali on 26/2/19.
// Copyright © 2019 Amir Kamali. All rights reserved.
//
@import UIKit;
@interface UIViewController (ExtendDealloc)
@end
.m file:
//
// UIViewController+ExtendDealloc.m
// extension
//
// Created by Amir Kamali on 26/2/19.
// Copyright © 2019 Amir Kamali. All rights reserved.
//
#import "UIViewController+ExtendDealloc.h"
#import <objc/runtime.h>
#import "test_objc-Swift.h"
@implementation UIViewController (ExtendDealloc)
#pragma mark - Swizzle Dealloc
+ (void)load {
// is this the best solution?
method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")),
class_getInstanceMethod(self.class, @selector(swizzledDealloc)));
}
- (void)swizzledDealloc {
[CustomBehaviorHandler printMe];
[self swizzledDealloc];
}
@end
Swift code:
import Foundation
import UIKit
class CustomBehaviorHandler:NSObject {
@objc
static func printMe() {
print("deinitializing ....")
}
}
[UPDATED]

Amir.n3t
- 2,859
- 3
- 21
- 28
-
1
-
Couldn't see any direct solution, however I updated the above code that indirectly calls swift codes. – Amir.n3t Feb 26 '19 at 06:12
-
You can't call +load anymore on any extension (here `UIViewController (ExtendDealloc)`) starting with Xcode 10.2 beta 3. – Cœur Feb 26 '19 at 12:34