Last active
December 20, 2020 08:08
-
-
Save karstenBriksoft/8e2f4c2bcda0a790ead36f89f8e03b88 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// | |
// TCObject.m | |
// | |
// | |
// Created by Karsten Kusche on 19.12.20. | |
// Copyright © 2020 briksoftware.com. All rights reserved. | |
// | |
// | |
// compile with -fno-objc-arc | |
// | |
// This code changes +alloc so that it initializes all inst-vars that have the type <id> to not be <nil> but [TCNull null] | |
// TCNull prints a Stack-Dump when a message is sent to it, that it doesn't understand. <nil> would silently return nil | |
// <nil> receivers are typically an indication of missing initialization and the error manifests a lot later | |
// with this fix it'll crash as soon as someone sends a message to an uninitialized variable | |
// | |
// don't use in production code, but during development it's sure helpful | |
// | |
#import "TCObject.h" | |
#import <objc/objc-runtime.h> | |
@interface TCNull : NSObject | |
+ (instancetype)null; | |
@end | |
Method orgAlloc = nil; | |
@implementation TCObject | |
+ (instancetype)tcAlloc | |
{ | |
// only handle TC* classes. System classes don't play well with this idea | |
const char* name = class_getName(self); | |
if (name[0] == 'T' && name[1] == 'C') | |
{ | |
IMP alloc = method_getImplementation(orgAlloc); | |
SEL allocSel = method_getName(orgAlloc); | |
TCObject* object = ((id(*)(id,SEL))alloc)(self,allocSel); | |
unsigned int outCount = 0; | |
Ivar* ivars = class_copyIvarList(self, &outCount); | |
for (NSInteger i = 0; i < outCount; i++) | |
{ | |
Ivar ivar = ivars[i]; | |
const char* type = ivar_getTypeEncoding(ivar); | |
if (*type == '@') | |
{ | |
object_setIvar(object, ivar, [TCNull null]); | |
} | |
} | |
free(ivars); | |
return object; | |
} | |
else | |
{ | |
IMP alloc = method_getImplementation(orgAlloc); | |
SEL allocSel = method_getName(orgAlloc); | |
return ((id(*)(id,SEL))alloc)(self,allocSel); | |
} | |
} | |
+ (void)load | |
{ | |
@autoreleasepool { | |
[TCNull null]; | |
[self swizzle]; | |
} | |
} | |
+ (void)swizzle | |
{ | |
Method alloc = class_getClassMethod(NSObject.class, @selector(alloc)); | |
Method tcAlloc = class_getClassMethod(self, @selector(tcAlloc)); | |
orgAlloc = tcAlloc; | |
method_exchangeImplementations(alloc, tcAlloc); | |
} | |
@end | |
@implementation TCNull | |
+ (instancetype)null | |
{ | |
static TCNull* null = nil; | |
if (null == nil) | |
{ | |
null = [[self alloc] init]; | |
} | |
return null; | |
} | |
- (void)doesNotRecognizeSelector:(SEL)aSelector | |
{ | |
raise(SIGINT); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment