Skip to content

Instantly share code, notes, and snippets.

@0xstragner
Last active October 20, 2022 15:47
Show Gist options
  • Save 0xstragner/ea86d5f457fb6d0f9ede3c459217441b to your computer and use it in GitHub Desktop.
Save 0xstragner/ea86d5f457fb6d0f9ede3c459217441b to your computer and use it in GitHub Desktop.
Medium/UISheetPresentationController/2.m
//
// SAPatternReference
//
@interface SAPatternReference : NSObject
@property (nonatomic, readonly, strong) NSString *name;
@property (nonatomic, readonly, assign) Class klass;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)referenceWithNamed:(NSString *)name
klass:(Class)klass;
@end
@implementation SAPatternReference
- (instancetype)initWithNamed:(NSString *)name
klass:(Class)klass;
{
self = [super init];
if (self != nil) {
_name = [name copy];
_klass = klass;
}
return self;
}
+ (instancetype)referenceWithNamed:(NSString *)name
klass:(Class)klass
{
return [[SAPatternReference alloc] initWithNamed:name
klass:klass];
}
@end
//
// SAClassRegistration
//
@interface SAClassRegistration : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithClassNamed:(NSString *)className
superclassNamed:(NSString *)superclassName;
- (Class)registerClass;
- (void)registerInstancePropertyWithReference:(SAPatternReference *)patternReference
getterExecutionBlock:(id)getterExecutionBlock
setterExecutionBlock:(id)setterExecutionBlock;
- (void)registerInstanceMethodWithReference:(SAPatternReference *)patternReference
executionBlock:(id)executionBlock;
@end
@interface SAClassRegistration ()
@property (nonatomic, readonly, assign) Class klass;
@property (nonatomic, readonly, assign) Class superklass;
@property (nonatomic, assign, getter=isKlassRegistered) BOOL klassRegistered;
@end
@implementation SAClassRegistration
- (instancetype)initWithClassNamed:(NSString *)className
superclassNamed:(NSString *)superclassName
{
self = [super init];
if (self != nil) {
Class superklass = NSClassFromString(superclassName);
if (superklass == NULL) {
[[NSException exceptionWithName:NSGenericException
reason:[NSString stringWithFormat:@"Class `%@` not found", className]
userInfo:nil] raise];
}
_superklass = superklass;
_klass = objc_allocateClassPair(_superklass, [className UTF8String], 0);
_klassRegistered = NO;
}
return self;
}
- (Class)registerClass {
if (!self.isKlassRegistered) {
objc_registerClassPair(self.klass);
}
return self.klass;
}
#pragma mark - Methods & Properties Registration
- (void)registerInstancePropertyWithReference:(SAPatternReference *)patternReference
getterExecutionBlock:(id)getterExecutionBlock
setterExecutionBlock:(id)setterExecutionBlock
{
[self registrationExceptionIfNeeded];
Class patternClass = patternReference.klass;
unsigned attributeListCount;
objc_property_t patternProperty = class_getProperty(patternClass, [patternReference.name UTF8String]);
objc_property_attribute_t *attributeList = property_copyAttributeList(patternProperty, &attributeListCount);
class_addProperty(self.klass, [patternReference.name UTF8String], attributeList, attributeListCount);
free(attributeList);
NSString *getterMethodName = patternReference.name;
[self registerInstanceMethodWithReference:[SAPatternReference referenceWithNamed:getterMethodName klass:patternClass]
executionBlock:getterExecutionBlock];
NSString *capitalizedName = [NSString stringWithFormat:@"%@%@", [[getterMethodName substringToIndex:1] uppercaseString], [getterMethodName substringFromIndex:1]];
NSString *setterMethodName = [NSString stringWithFormat:@"set%@:", capitalizedName];
[self registerInstanceMethodWithReference:[SAPatternReference referenceWithNamed:setterMethodName klass:patternClass]
executionBlock:setterExecutionBlock];
}
- (void)registerInstanceMethodWithReference:(SAPatternReference *)patternReference
executionBlock:(id)executionBlock
{
[self registrationExceptionIfNeeded];
SEL patternSelector = NSSelectorFromString(patternReference.name);
const char *patternTypeEncoding = method_getTypeEncoding(class_getInstanceMethod(patternReference.klass, patternSelector));
class_addMethod(self.klass, patternSelector, imp_implementationWithBlock(executionBlock), patternTypeEncoding);
}
#pragma mark - Private
- (void)registrationExceptionIfNeeded {
if (!self.isKlassRegistered) {
return;
}
[[NSException exceptionWithName:NSGenericException
reason:@"Class already registered and can not being modified."
userInfo:nil] raise];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment