OCFuntime is a toolkit for Objective-C runtime.
- Change instance or class method implementation and revert it back
- Inject property of any type to any class
- Auto injection of protocol optional method
- Modular structure: each task extracted as Cocoapods subspec
Method implementation changing allows to run block of code on corresponding method call. Don't be confused with method swizzling.
Instance method implementation block should conform signature:
method_return_type ^(id selfInstance, arg1_type arg1, arg2_type arg2, ...)
Class method implementation block should conform signature:
method_return_type ^(Class theClass, arg1_type arg1, arg2_type arg2, ...)
Installation
Add pod 'OCFuntime/Methods'
to Podfile
Example
@interface MyClass : NSObject
- (void)someInstanceMethod:(NSObject *)someArg;
+ (NSInteger)someClassMethod;
@end
#import "OCFuntime+Methods.h"
...
OCFuntime *funtime = [[OCFuntime alloc] init];
// Change instance method implementation
[funtime changeClass:MyClass.class instanceMethod:@selector(someInstanceMethod:)
implementation:^(MyClass *instance, NSObject *someArg)
{
NSLog(@"Changed instance method with arg %@!", someArg);
}];
// Change class method implementation
[funtime changeClass:MyClass.class classMethod:@selector(someClassMethod)
implementation:^(Class theClass)
{
NSLog(@"Changed class method of %@!", NSStringFromClass(theClass));
return 5;
}];
//Revert method to default implementation
[funtime revertClass:MyClass.class instanceMethod:@selector(someInstanceMethod)];
[funtime revertClass:MyClass.class classMethod:@selector(someClassMethod)];
// Revert all methods of class to default implementation
[funtime revertClassMethods:MyClass.class];
// Revert all changed methods to default implementation
[funtime revertAllMethods];
Notes
- Skip arguments in implementation block signature if you don't need them.
- Skipping value return will cause undefined behaviour.
- After
OCFuntime
instance will be deallocated all changed methods will be reverted to default implementations. To avoid it use Shared Instance ofOCFuntime
. - Changing unexisted method will rise an exception.
- Changing implementation isn't thread safe.
NSObject Category
Run method implementation changing on corresponding class with NSObject
category.
Add pod 'OCFuntime/NSObject+OCFMethods'
to Podfile
#import "NSObject+OCFMethods.h"
...
// Change class method implementation
[MyClass changeClassMethod:@selector(someClassMethod) implementation:^
{
NSLog(@"Changed 'someClassMethod'");
return 0;
}];
// Change instance method implementation
[MyClass changeInstanceMethod:@selector(someInstanceMethod)
implementation:^(MyClass *instance, NSObject *someArg)
{
NSLog(@"Called changed method of %@ with arg %@", instance, someArg);
}];
// Revert class method implementation
[MyClass revertClassMethod:@selector(someStaticMethod)];
// Revert instance method implementation
[MyClass revertInstanceMethod:@selector(someMethod)];
// Revert all methods
[MyClass revertMethods];
NSObject+OCFMethods
subspec includes Methods
and Shared
subspecs as dependencies. Don't include them to Podfile.
Property injection allow to use @dynamic
properties as it've been @synthesize
.
And it's the best way to avoid "no synthesized properties in objective-c categories" restriction.
Injection is based on Message Forwarding and Associated Objects.
Installation
Add pod 'OCFuntime/Properties'
to Podfile
Example
@interface SomeClass : NSObject
@property (nonatomic, strong) id objectStrongProperty;
@property (nonatomic, assign) NSInteger integerProperty;
@end
...
@implementation
@dynamic objectStrongProperty, integerProperty;
@end
...
#import "OCFuntime+Properties.h"
#import "SomeClass.h"
...
// Inject properties
OCFuntime *funtime = [[OCFuntime alloc] init];
[funtime injectClass:SomeClass.class property:@"objectStrongProperty"];
[funtime injectClass:SomeClass.class property:@"integerProperty"];
// Use properties
SomeClass *someInstance = [[SomeClass alloc] init];
someInstance.objectStrongProperty = [[NSObject alloc] init];
someInstance.integerProperty = 5;
// Remove injected property
[funtime removeClass:SomeClass.class property:@"objectStrongProperty"];
// Remove all class injected properties
[funtime removeClassProperties:SomeClass.class];
// Remove all injected properties
[funtime removeAllProperties];
Notes
atomic
properties injected asnonatomic
. It's will be fixed in one of the next releases.- After
OCFuntime
instance will be deallocated all injected properties will be removed. To avoid it use Shared Instance ofOCFuntime
. - If property doesn't defined in class interface exception will raise.
- If property synthesized or already injected exception will raise.
- Property injection doesn't break methods
forwardInvocation:
andmethodSignatureForSelector:
because of Swizzling. - Property injection isn't thread safe.
NSObject Category
Run property injection on corresponding class with NSObject
category.
Add pod 'OCFuntime/NSObject+OCFProperties'
to Podfile
#import "NSObject+OCFProperties.h"
...
// Inject properties
[SomeClass injectProperty:@"objectStrongProperty"];
[SomeClass injectProperty:@"integerProperty"];
// Remove property
[SomeClass removeProperty:@"objectStrongProperty"];
// Remove all injected properties of class
[SomeClass removeProperties];
NSObject+OCFProperties
subspec includes Properties
and Shared
subspecs as dependencies. Don't include them to Podfile.
Auto injection of protocol optional method allows to inject method with block to all classes that conform protocol. To override existing implementation or previous injection use forceInject
method.
Installation
Add pod 'OCFuntime/Protocols'
to Podfile
Example
#import "OCFAutoInjectProtocol.h"
@protocol SomeProtocol <OCFAutoInjectProtocol>
- (BOOL)instanceMethod:(BOOL)arg;
+ (BOOL)classMethod:(BOOL)arg;
@end
...
@interface SomeClass : NSObject <SomeProtocol>
@end
...
@interface AnotherClass : NSObject <SomeProtocol>
@end
#import "OCFuntime+Protocols.h"
...
OCFuntime *funtime = [[OCFuntime alloc] init];
[funtime injectProtocol:@protocol(SomeProtocol) instanceMethod:@selector(instanceMethod:)
implementation:^(id selfInstance, BOOL argument)
{
return argument;
}];
[funtime injectProtocol:@protocol(SomeProtocol) classMethod:@selector(classMethod:)
implementation:^(Class theClass, BOOL argument)
{
return !argument;
}];
...
SomeClass *someInstance = [[SomeClass alloc] init];
AnotherClass *anotherInstance = [[AnotherClass alloc] init];
[someInstance instanceMethod:YES]; // YES
[anotherInstance instanceMethod:YES]; // YES
[SomeClass classMethod:YES]; // NO;
[AnotherClass classMethod:YES]; // NO;
Notes
- Injection of '@required' methods will raise exception
Shared instance
Add pod 'OCFuntime/Shared'
to Podfile
#import "OCFuntime+Shared.h"
...
[OCFuntime.shared changeClass:MyClass.class
instanceMethod:@selector(someMethod)]
implementation:^
{
NSLog(@"Changed someMethod with shared instance")
}];
Default subspec
Default subspec pod 'OCFuntime'
includes all subspecs. Use @import "OCFuntimeHeader.h"
to enable all features of OCFuntime
.
Follow updates @okolodev