The title is the question's formulation - i.e. what are the patterns and anti-patterns of +initialize and +load class methods overriding?
Have you met particular examples? If yes - please describe.
P.S. There was some good Q&A on +load and +initialize here on StackOverflow but no one tells about the practical interest of these methods. Mechanisms were discussed.
+load is useful for setting up stuff needed for categories because all the +load methods are guaranteed to be called once each when the binary is loaded (even if there are multiple +load methods for the same class, which normally would replace one another). Inheritance is actually irrelevant to its functioning.
I almost never use +load, but +initialize is useful for all sorts of things... setting up static variables, dynamically loading libraries for plugin architectures... anything you want to do one time like printing version info, setting up a global instance to do something specialized, like for logging, crash reporter, signal handler, etc...
edit:
to prevent multiple initialize calls from messing stuff up (which happens to superclasses when the child class is used after a superclass): you can make it reentrant (this is a common pattern):
+(void) initialize {
static BOOL inited = NO;
if(!inited)
{
/*dostuff*/
inited=YES;
}
}
A good use of the load method is to initialize global variables that can't be initialized at compile-time.
Here is a made up example:
SomeClass.h
extern NSString *SomeGlobalConstant;
// Followed by some class interface stuff
SomeClass.m
#import "SomeClass.h"
NSString *SomeGlobalConstant = nil;
static NSArray *someFileStaticArray = nil;
#implementation SomeClass
+ (void)load {
if (self == [SomeClass class]) {
SomeGlobalConstant = #"SomeAppropriateValue";
someFileStaticArray = #[ #"A", #"B", #"C" ];
}
}
// and the rest of the class implementation
#end
Another possible use of +initialize is for Method Swizzling. Which you shouldn't really use unless you're sure you know what you're doing. Read this SO question for more details.
It allows you to substitute an existing method implementation with your own, and still be able to call the original one. For instance, for faking NSDate in unit tests you could write something like this (note, that there are other ways to do this (OCMock, etc.), this is just an example). This code allows you to set a program-wide fake NSDate, which will be returned whenever [NSDate date] is called. And if no fake date is set, then the original implementation is used.
#import "NSDate+UnitTest.h"
#import "MethodSwizzling.h"
#implementation NSDate(UnitTest)
static NSDate *fakeCurrentDate = nil;
+(void)setFakeCurrentDate:(NSDate *)date
{
fakeCurrentDate = date;
}
+(NSDate *)fakeCurrentDate
{
if (fakeCurrentDate) {
return fakeCurrentDate;
}
else {
NSDate *result = [self fakeCurrentDate];
return result;
}
}
+(void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(#"Swizzling...");
SwizzleClassMethod([self class], #selector(date), #selector(fakeCurrentDate));
});
}
#end
//MethodSwizzling.m:
void SwizzleMethod(Class c, SEL orig, SEL new, BOOL isClassMethod) {
NSLog(#"Swizzling %# method %# of class %# with fake selector %#.",
(isClassMethod ? #"a class" : #"an instance"),
NSStringFromSelector(orig),
NSStringFromClass(c),
NSStringFromSelector(new));
Method origMethod = isClassMethod ? class_getClassMethod(c, orig) : class_getInstanceMethod(c, orig);
Method newMethod = isClassMethod ? class_getClassMethod(c, new) : class_getInstanceMethod(c, new);
method_exchangeImplementations(origMethod, newMethod);
//Actually, it's better to do it using C-functions instead of Obj-C methods and
//methos_setImplementation instead of method_exchangeImplementation, but since this
//is not an open-source project and these components aren't going to be used by other people,
//it's fine. The problem is that method_exchangeImplementations will mess things up if
//the implementation relies on a fact that the selector passed as a _cmd parameter
//matches the function name.
//More about it: http://blog.newrelic.com/2014/04/16/right-way-to-swizzle/
}
void SwizzleClassMethod(Class c, SEL orig, SEL new) {
SwizzleMethod(c, orig, new, YES);
}
void SwizzleInstanceMethod(Class c, SEL orig, SEL new) {
SwizzleMethod(c, orig, new, NO);
}
Related
I am using OCMock 3 to unit test my iOS project.
I use dispatch_once() created a singleton class MyManager :
#implementation MyManager
+ (id)sharedInstance {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
I have a method in School class which uses the above singleton:
#implementation School
...
- (void) createLecture {
MyManager *mgr = [MyManager sharedInstance];
[mgr checkLectures];
...
}
#end
Now, I want to unit test this method, I use a partial mock of MyManager:
- (void) testCreateLecture {
// create a partially mocked instance of MyManager
id partialMockMgr = [OCMockObject partialMockForObject:[MyManager sharedInstance]];
// run method to test
[schoolToTest createLecture];
...
}
- (void)tearDown {
// I want to set the singleton instance to nil, how to?
[super tearDown];
}
In tearDown phase, I want to set the singleton instance to nil so that the following test case could start from clean state.
I know on internet, some people suggest to move the static MyManager *sharedMyManager outside the +(id)sharedInstance method. But I would like to ask, is there any way to set the instance to nil without moving it outside +(id)sharedInstance method? (Any solution like java reflection?)
You can't achieve what you want with a local static variable. Block-scoped statics are only visible inside their lexical context.
We do this by making the singleton instance a static variable scoped to the class implementation and adding a mutator to override it. Generally that mutator is only called by tests.
#implementation MyManager
static MyManager *_sharedInstance = nil;
static dispatch_once_t once_token = 0;
+(instancetype)sharedInstance {
dispatch_once(&once_token, ^{
if (_sharedInstance == nil) {
_sharedInstance = [[MyManager alloc] init];
}
});
return _sharedInstance;
}
+(void)setSharedInstance:(MyManager *)instance {
once_token = 0; // resets the once_token so dispatch_once will run again
_sharedInstance = instance;
}
#end
Then in your unit test:
// we can replace it with a mock object
id mockManager = [OCMockObject mockForClass:[MyManager class]];
[MyManager setSharedInstance:mockManager];
// we can reset it so that it returns the actual MyManager
[MyManager setSharedInstance:nil];
This also works with partial mocks, as in your example:
id mockMyManager = [OCMockObject partialMockForObject:[MyManager sharedInstance]];
[[mockMyManager expect] checkLectures];
[MyManager setSharedInstance:mockMyManager];
[schoolToTest createLecture];
[mockMyManager verify];
[mockMyManager stopMocking];
// reset it so that it returns the actual MyManager
[MyManager setSharedInstance:nil];
Here's a full breakdown of the approach.
The answer is no, because you use dispatch_once(&onceToken, ^{ so even if you added another method which could reset the variable to nil you'd never be able to initialise it again.
So you already have one solution and the best solution is to not access the singleton directly (use dependency injection instead).
It is an easier way to solute your issue.
Your class have a singleton. you can add a method that is destroy this class instance. So when you call shareManager method again , it will create a new instance.
Such as:
static MyManager *sharedMyManager = nil;
+ (void)destroy
{
sharedMyManager = nil;
}
As others have stated, what you should really do is refactor your code to use dependency injection. This means that if the School class needs a MyManager instance to operate, then it should have an initWithManager:(MyManager *)manager method which should be the designated initializer. Or if the MyManager is only needed in this particular method, it should be a method parameter, e.g. createLectureWithManager:(MyManager *)manager.
Then in your tests, you could just do School *schoolToTest = [[School alloc] initWithManager:[[MyManager alloc] init]], and each test would have a new MyManager instance. You could drop the singleton pattern entirely, removing the sharedInstance method on MyManager and your application's logic would be responsible to ensure that there is only one instance that you pass around.
But sometimes, you have to work with legacy code that you can't just refactor. In these cases, you need to stub the class method. That is, you need to replace the implementation of -[MyManager sharedInstance] with an implementation that returns [[MyManager alloc] init]. This can be accomplished using the runtime to swizzle the class method, which would be the equivalent of Java reflection that you are looking for. See this for an example of how to use the runtime.
You can also do it with OCMock, which uses the runtime behind the scenes, just like mocking frameworks in Java are based on the reflection API :
MyManager *testManager = [[MyManager alloc] init];
id mock = [[OCMockObject mockForClass:[MyManager class]];
[[[mock stub] andReturn:testManager] sharedInstance];
If you don't want to refactor your code for easier unit testing then there is another solution (not perfect but works):
Create a local property of MyManager type
In setUp instantiate the property from above and swizzle the sharedInstance method with your local method (e.g. swizzle_sharedInstance)
Inside the swizzle_sharedInstance return the local property
In tearDown swizzle back to original sharedInstance and nullify the local property
I suggest a little bit different approach. You can create a mock of your sharedInstance using OCMock:
id myManagerMock = OCMClassMock([MyManager class]);
OCMStub([myManagerMock sharedManager]).andReturn(myManagerMock);
Now School implementation will use myManagerMock object, and you can stub this object to return anything you want under you test case. For example:
OCMStub([myManagerMock someMethodThatReturnsBoolean]).andReturn(YES);
It's important that after your tests, you will perform cleaning of your mock object by calling (at the end of your test method or in -tearDown):
[myManagerMock stopMocking];
i want create 3 or more different singleton to handle different store scenario in my app using FMDB, an example of a singleton is this:
.h
#interface MyManager : NSObject
+ (id)sharedManager;
- (BOOL)isChecked:(int)id_product;
#end
.m
#implementation MyManager
#synthesize someProperty;
#pragma mark Singleton Methods
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
self.databaseQueue = [FMDatabaseQueue databaseQueueWithPath:databasePath]; //or FMDatabase
}
return self;
}
- (BOOL)isChecked:(int)id_product
{
BOOL isChecked = NO;
[self.databaseQueue inDatabase:^(FMDatabase *db) {
FMResultSet *product_query = [db executeQuery:#"SELECT isChecked FROM products WHERE id = ?",[NSNumber numberWithInt:id_product]];
while ([product_query next]) {
if ([product_query boolForColumn:#"isChecked"] == 1) {
isChecked = YES;
} else {
isChecked = NO;
}
}
}];
return isChecked;
}
#end
So my question is, can i create 3 or more singleton like this that use a FMDatabaseQueue or a FMDatabase defined as a class property for the class, and is better using FMDatabaseQueue or FMDatabase?
A few thoughts:
You theoretically can have three different classes of singleton objects, each with its own FMDatabase/FMDatabaseQueue instance.
Whether you should do this is a completely different question. Having three, without some very compelling argument for that, suggests some serious code smell.
Make sure that none of those three instances are trying to access the same database file, or else you defeat the entire purpose of FMDatabaseQueue. This three instance model is only plausible if you're dealing with three different database files (and even then, it seems like a curious design).
You say "i think i can use only one singleton, the idea of use different singleton was only to make code more readable, and divide the operation for type."
That is, absolutely, not an argument for three singleton classes. you should only have one.
In terms of FMDatabase vs. FMDatabaseQueue, the latter enables you to enjoy multithreaded access, so I would lean towards that. Using FMDatabase offers no significant advantages, but introduces limitations unnecessarily.
The whole purpose of FMDatabaseQueue is to manage database contention when you have multiple threads accessing the same database. So if you know, with absolute certainty, that you'll never access FMDatabase object from different threads, then you could use FMDatabase.
But why paint yourself into a corner like that? Just use FMDatabaseQueue and then you don't have to worry about it. It works fine if used from single thread and saves you from many headaches if you happen to use it from multiple threads (e.g. you use your instances from GCD blocks, inside completion handlers for asynchronous methods, etc.).
I love blocks and it makes me sad when I can't use them. In particular, this happens mostly every time I use delegates (e.g.: with UIKit classes, mostly pre-block functionality).
So I wonder... Is it possible -using the crazy power of ObjC-, to do something like this?
// id _delegate; // Most likely declared as class variable or it will be released
_delegate = [DelegateFactory delegateOfProtocol:#protocol(SomeProtocol)];
_delegate performBlock:^{
// Do something
} onSelector:#selector(someProtocolMethod)]; // would execute the given block when the given selector is called on the dynamic delegate object.
theObject.delegate = (id<SomeProtocol>)_delegate;
// Profit!
performBlock:onSelector:
If YES, how? And is there a reason why we shouldn't be doing this as much as possible?
Edit
Looks like it IS possible. Current answers focus on the first part of the question, which is how. But it'd be nice to have some discussion on the "should we do it" part.
Okay, I finally got around to putting WoolDelegate up on GitHub. Now it should only take me another month to write a proper README (although I guess this is a good start).
The delegate class itself is pretty straightforward. It simply maintains a dictionary mapping SELs to Block. When an instance recieves a message to which it doesn't respond, it ends up in forwardInvocation: and looks in the dictionary for the selector:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = [anInvocation selector];
GenericBlock handler = [self handlerForSelector:sel];
If it's found, the Block's invocation function pointer is pulled out and passed along to the juicy bits:
IMP handlerIMP = BlockIMP(handler);
[anInvocation Wool_invokeUsingIMP:handlerIMP];
}
(The BlockIMP() function, along with other Block-probing code, is thanks to Mike Ash. Actually, a lot of this project is built on stuff I learned from his Friday Q&A's. If you haven't read those essays, you're missing out.)
I should note that this goes through the full method resolution machinery every time a particular message is sent; there's a speed hit there. The alternative is the path that Erik H. and EMKPantry each took, which is creating a new clas for each delegate object that you need, and using class_addMethod(). Since every instance of WoolDelegate has its own dictionary of handlers, we don't need to do that, but on the other hand there's no way to "cache" the lookup or the invocation. A method can only be added to a class, not to an instance.
I did it this way for two reasons: this was an excercise to see if I could work out the part that's coming next -- the hand-off from NSInvocation to Block invocation -- and the creation of a new class for every needed instance simply seemed inelegant to me. Whether it's less elegant than my solution, I will leave to each reader's judgement.
Moving on, the meat of this procedure is actually in the NSInvocation category that's found in the project. This utilizes libffi to call a function that's unknown until runtime -- the Block's invocation -- with arguments that are also unknown until runtime (which are accessible via the NSInvocation). Normally, this is not possible, for the same reason that a va_list cannot be passed on: the compiler has to know how many arguments there are and how big they are. libffi contains assembler for each platform that knows/is based on those platforms' calling conventions.
There's three steps here: libffi needs a list of the types of the arguments to the function that's being called; it needs the argument values themselves put into a particular format; then the function (the Block's invocation pointer) needs to be invoked via libffi and the return value put back into the NSInvocation.
The real work for the first part is handled largely by a function which is again written by Mike Ash, called from Wool_buildFFIArgTypeList. libffi has internal structs that it uses to describe the types of function arguments. When preparing a call to a function, the library needs a list of pointers to these structures. The NSMethodSignature for the NSInvocation allows access of each argument's encoding string; translating from there to the correct ffi_type is handled by a set of if/else lookups:
arg_types[i] = libffi_type_for_objc_encoding([sig getArgumentTypeAtIndex:actual_arg_idx]);
...
if(str[0] == #encode(type)[0]) \
{ \
if(sizeof(type) == 1) \
return &ffi_type_sint8; \
else if(sizeof(type) == 2) \
return &ffi_type_sint16; \
Next, libffi wants pointers to the argument values themselves. This is done in Wool_buildArgValList: get the size of each argument, again from the NSMethodSignature, and allocate a chunk of memory that size, then return the list:
NSUInteger arg_size;
NSGetSizeAndAlignment([sig getArgumentTypeAtIndex:actual_arg_idx],
&arg_size,
NULL);
/* Get a piece of memory that size and put its address in the list. */
arg_list[i] = [self Wool_allocate:arg_size];
/* Put the value into the allocated spot. */
[self getArgument:arg_list[i] atIndex:actual_arg_idx];
(An aside: there's several notes in the code about skipping over the SEL, which is the (hidden) second passed argument to any method invocation. The Block's invocation pointer doesn't have a slot to hold the SEL; it just has itself as the first argument, and the rest are the "normal" arguments. Since the Block, as written in client code, could never access that argument anyways (it doesn't exist at the time), I decided to ignore it.)
libffi now needs to do some "prep"; as long as that succeeds (and space for the return value can be allocated), the invocation function pointer can now be "called", and the return value can be set:
ffi_call(&inv_cif, (genericfunc)theIMP, ret_val, arg_vals);
if( ret_val ){
[self setReturnValue:ret_val];
free(ret_val);
}
There's some demonstrations of the functionality in main.m in the project.
Finally, as for your question of "should this be done?", I think the answer is "yes, as long as it makes you more productive". WoolDelegate is completely generic, and an instance can act like any fully written-out class. My intention for it, though, was to make simple, one-off delegates -- that only need one or two methods, and don't need to live past their delegators -- less work than writing a whole new class, and more legible/maintainable than sticking some delegate methods into a view controller because it's the easiest place to put them. Taking advantage of the runtime and the language's dynamism like this hopefully can increase your code's readability, in the same way, e.g., Block-based NSNotification handlers do.
I just put together a little project that lets you do just this...
#interface EJHDelegateObject : NSObject
+ (id)delegateObjectForProtocol:(Protocol*) protocol;
#property (nonatomic, strong) Protocol *protocol;
- (void)addImplementation:(id)blockImplementation forSelector:(SEL)selector;
#end
#implementation EJHDelegateObject
static NSInteger counter;
+ (id)delegateObjectForProtocol:(Protocol *)protocol
{
NSString *className = [NSString stringWithFormat:#"%s%#%i",protocol_getName(protocol),#"_EJH_implementation_", counter++];
Class protocolClass = objc_allocateClassPair([EJHDelegateObject class], [className cStringUsingEncoding:NSUTF8StringEncoding], 0);
class_addProtocol(protocolClass, protocol);
objc_registerClassPair(protocolClass);
EJHDelegateObject *object = [[protocolClass alloc] init];
object.protocol = protocol;
return object;
}
- (void)addImplementation:(id)blockImplementation forSelector:(SEL)selector
{
unsigned int outCount;
struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(self.protocol, NO, YES, &outCount);
struct objc_method_description description;
BOOL descriptionFound = NO;
for (int i = 0; i < outCount; i++){
description = methodDescriptions[i];
if (description.name == selector){
descriptionFound = YES;
break;
}
}
if (descriptionFound){
class_addMethod([self class], selector, imp_implementationWithBlock(blockImplementation), description.types);
}
}
#end
And using an EJHDelegateObject:
self.alertViewDelegate = [EJHDelegateObject delegateObjectForProtocol:#protocol(UIAlertViewDelegate)];
[self.alertViewDelegate addImplementation:^(id _self, UIAlertView* alertView, NSInteger buttonIndex){
NSLog(#"%# dismissed with index %i", alertView, buttonIndex);
} forSelector:#selector(alertView:didDismissWithButtonIndex:)];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Example" message:#"My delegate is an EJHDelegateObject" delegate:self.alertViewDelegate cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
[alertView show];
Edit: This is what I've come up after having understood your requirement. This is just a quick hack, an idea to get you started, it's not properly implemented, nor is it tested. It is supposed to work for delegate methods that take the sender as their only argument. It works It is supposed to work with normal and struct-returning delegate methods.
typedef void *(^UBDCallback)(id);
typedef void(^UBDCallbackStret)(void *, id);
void *UBDDelegateMethod(UniversalBlockDelegate *self, SEL _cmd, id sender)
{
UBDCallback cb = [self blockForSelector:_cmd];
return cb(sender);
}
void UBDelegateMethodStret(void *retadrr, UniversalBlockDelegate *self, SEL _cmd, id sender)
{
UBDCallbackStret cb = [self blockForSelector:_cmd];
cb(retaddr, sender);
}
#interface UniversalBlockDelegate: NSObject
- (BOOL)addDelegateSelector:(SEL)sel isStret:(BOOL)stret methodSignature:(const char *)mSig block:(id)block;
#end
#implementation UniversalBlockDelegate {
SEL selectors[128];
id blocks[128];
int count;
}
- (id)blockForSelector:(SEL)sel
{
int idx = -1;
for (int i = 0; i < count; i++) {
if (selectors[i] == sel) {
return blocks[i];
}
}
return nil;
}
- (void)dealloc
{
for (int i = 0; i < count; i++) {
[blocks[i] release];
}
[super dealloc];
}
- (BOOL)addDelegateSelector:(SEL)sel isStret:(BOOL)stret methodSignature:(const char *)mSig block:(id)block
{
if (count >= 128) return NO;
selectors[count] = sel;
blocks[count++] = [block copy];
class_addMethod(self.class, sel, (IMP)(stret ? UBDDelegateMethodStret : UBDDelegateMethod), mSig);
return YES;
}
#end
Usage:
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
UniversalBlockDelegate *d = [[UniversalBlockDelegate alloc] init];
webView.delegate = d;
[d addDelegateSelector:#selector(webViewDidFinishLoading:) isStret:NO methodSignature:"v#:#" block:^(id webView) {
NSLog(#"Web View '%#' finished loading!", webView);
}];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]]];
I need to add additional behavior to methods I need to extend, i.e. implement method that looks like
- (void)extendMethod:(SEL)selector forClass:(Class)class withCompletionBlock:(void (^)(void))completionBlock;
So every time Class instance call a method with SEL selector in addition should be invoked my completion block.
I've tried method swizzling, but ran into some problems: I want original method implementation to be called.
What I need is very similar with subclassing, but this should be implemented without subclassing.
UPDATE:
For example I have subclass of UIViewController named MyViewController. MyViewController have - (void)viewDidLoad method. Somewhere in application I call method
[methodExtender extendMethod:#selector(viewDidLoad)
forClass:[MyViewController class]
withCompletionBlock:^{
NSLog(#"view did load called");
}];
So after viewDidLoad method of every instance of MyViewController my completion block invoked.
I'm not sure how you want to use selector, but you can try extend any class(even ones that you don't have implementation file) by using mechanism in Objective-C know as "Categories".
From Xcode click on File->New->File (command+n)
From Cocoa Touch choose Objective-C category
Type name of your category and choose class on which you want to make category (I choosed UIButton)
Then next and Create.
Xcode will create two files for example: UIButton+extendMethod.h
Declare your method in header file and implement it in *.m file.
Using
If you want to use in let's say your View Controller in *.h file import
#import "UIButton+extendMethod.h"
and then you can call your method like this:
UIButton *button = [[UIButton alloc] init];
[button extendMethod:#selector(yourMethod:)];
Swizzling does allow you to call the original implementation, though it is just a bit confusing. Because the implementations are swapped after swizzling, you call the original implementation using the selector of the swizzled method:
- (void)mySwizzledImplementation {
// do stuff
// now call original implementation (using swizzled selector!)
[self mySwizzledImplementation];
// do more stuff
}
See also http://cocoadev.com/wiki/MethodSwizzling
There is no way (anymore)to simulate inheritance without subclassing. There use to be Posing, method swizzling is all that is left (not as elegant as posing though). Here is one way to do method swizzling correctly while being able to call the original implementation.
int swizzle_instance_methods(Class class, SEL selector, IMP replacement, IMP *store) {
#synchronized(class) {
Method method = class_getInstanceMethod(class, selector);
IMP original_imp = NULL;
if (method != NULL) {
const char *type = method_getTypeEncoding(method);
IMP original_imp = class_replaceMethod(class, selector, replacement, type);
if (original_imp == NULL)
original_imp = method_getImplementation(method);
if (original_imp != NULL && store != NULL) {
*store = original_imp;
}
}
return (original_imp != NULL);
}
}
+ (void) load
{
static IMP originalMethodImpl = NULL;
IMP customMethodImpl = imp_implementationWithBlock(^(id self_) {
NSString *descr = ((NSString(*)(id,SEL))originalMethodImpl)(self_, #selector(description);
return [NSString stringWithFormat:#"<--- %# --->",descr];
});
swizzle_instance_methods([self class], #selector(description), customMethodImpl, &originalMethodImpl);
}
I might add that this is nice for debugging and I think that it can be greate for building excellent frameworks. Alas, Apple seems to think differently and using method swizzling can result in your app being excluded from the App store. If you are not aiming for the app store then all the power to you.
It is be possible with ObjC categories. For ex, you can extend hasPrefix method of NSString as follows,
-(BOOL)hasPrefixx:(NSString *)aString
{
NSLog(#"Checking has prefix");
return [self hasPrefix:aString];
}
If you import the category, you should be able to call this method. But his means you got change the method in your call.
By the way, Method swizzling should work. Bit of explanation here.
I love blocks and it makes me sad when I can't use them. In particular, this happens mostly every time I use delegates (e.g.: with UIKit classes, mostly pre-block functionality).
So I wonder... Is it possible -using the crazy power of ObjC-, to do something like this?
// id _delegate; // Most likely declared as class variable or it will be released
_delegate = [DelegateFactory delegateOfProtocol:#protocol(SomeProtocol)];
_delegate performBlock:^{
// Do something
} onSelector:#selector(someProtocolMethod)]; // would execute the given block when the given selector is called on the dynamic delegate object.
theObject.delegate = (id<SomeProtocol>)_delegate;
// Profit!
performBlock:onSelector:
If YES, how? And is there a reason why we shouldn't be doing this as much as possible?
Edit
Looks like it IS possible. Current answers focus on the first part of the question, which is how. But it'd be nice to have some discussion on the "should we do it" part.
Okay, I finally got around to putting WoolDelegate up on GitHub. Now it should only take me another month to write a proper README (although I guess this is a good start).
The delegate class itself is pretty straightforward. It simply maintains a dictionary mapping SELs to Block. When an instance recieves a message to which it doesn't respond, it ends up in forwardInvocation: and looks in the dictionary for the selector:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = [anInvocation selector];
GenericBlock handler = [self handlerForSelector:sel];
If it's found, the Block's invocation function pointer is pulled out and passed along to the juicy bits:
IMP handlerIMP = BlockIMP(handler);
[anInvocation Wool_invokeUsingIMP:handlerIMP];
}
(The BlockIMP() function, along with other Block-probing code, is thanks to Mike Ash. Actually, a lot of this project is built on stuff I learned from his Friday Q&A's. If you haven't read those essays, you're missing out.)
I should note that this goes through the full method resolution machinery every time a particular message is sent; there's a speed hit there. The alternative is the path that Erik H. and EMKPantry each took, which is creating a new clas for each delegate object that you need, and using class_addMethod(). Since every instance of WoolDelegate has its own dictionary of handlers, we don't need to do that, but on the other hand there's no way to "cache" the lookup or the invocation. A method can only be added to a class, not to an instance.
I did it this way for two reasons: this was an excercise to see if I could work out the part that's coming next -- the hand-off from NSInvocation to Block invocation -- and the creation of a new class for every needed instance simply seemed inelegant to me. Whether it's less elegant than my solution, I will leave to each reader's judgement.
Moving on, the meat of this procedure is actually in the NSInvocation category that's found in the project. This utilizes libffi to call a function that's unknown until runtime -- the Block's invocation -- with arguments that are also unknown until runtime (which are accessible via the NSInvocation). Normally, this is not possible, for the same reason that a va_list cannot be passed on: the compiler has to know how many arguments there are and how big they are. libffi contains assembler for each platform that knows/is based on those platforms' calling conventions.
There's three steps here: libffi needs a list of the types of the arguments to the function that's being called; it needs the argument values themselves put into a particular format; then the function (the Block's invocation pointer) needs to be invoked via libffi and the return value put back into the NSInvocation.
The real work for the first part is handled largely by a function which is again written by Mike Ash, called from Wool_buildFFIArgTypeList. libffi has internal structs that it uses to describe the types of function arguments. When preparing a call to a function, the library needs a list of pointers to these structures. The NSMethodSignature for the NSInvocation allows access of each argument's encoding string; translating from there to the correct ffi_type is handled by a set of if/else lookups:
arg_types[i] = libffi_type_for_objc_encoding([sig getArgumentTypeAtIndex:actual_arg_idx]);
...
if(str[0] == #encode(type)[0]) \
{ \
if(sizeof(type) == 1) \
return &ffi_type_sint8; \
else if(sizeof(type) == 2) \
return &ffi_type_sint16; \
Next, libffi wants pointers to the argument values themselves. This is done in Wool_buildArgValList: get the size of each argument, again from the NSMethodSignature, and allocate a chunk of memory that size, then return the list:
NSUInteger arg_size;
NSGetSizeAndAlignment([sig getArgumentTypeAtIndex:actual_arg_idx],
&arg_size,
NULL);
/* Get a piece of memory that size and put its address in the list. */
arg_list[i] = [self Wool_allocate:arg_size];
/* Put the value into the allocated spot. */
[self getArgument:arg_list[i] atIndex:actual_arg_idx];
(An aside: there's several notes in the code about skipping over the SEL, which is the (hidden) second passed argument to any method invocation. The Block's invocation pointer doesn't have a slot to hold the SEL; it just has itself as the first argument, and the rest are the "normal" arguments. Since the Block, as written in client code, could never access that argument anyways (it doesn't exist at the time), I decided to ignore it.)
libffi now needs to do some "prep"; as long as that succeeds (and space for the return value can be allocated), the invocation function pointer can now be "called", and the return value can be set:
ffi_call(&inv_cif, (genericfunc)theIMP, ret_val, arg_vals);
if( ret_val ){
[self setReturnValue:ret_val];
free(ret_val);
}
There's some demonstrations of the functionality in main.m in the project.
Finally, as for your question of "should this be done?", I think the answer is "yes, as long as it makes you more productive". WoolDelegate is completely generic, and an instance can act like any fully written-out class. My intention for it, though, was to make simple, one-off delegates -- that only need one or two methods, and don't need to live past their delegators -- less work than writing a whole new class, and more legible/maintainable than sticking some delegate methods into a view controller because it's the easiest place to put them. Taking advantage of the runtime and the language's dynamism like this hopefully can increase your code's readability, in the same way, e.g., Block-based NSNotification handlers do.
I just put together a little project that lets you do just this...
#interface EJHDelegateObject : NSObject
+ (id)delegateObjectForProtocol:(Protocol*) protocol;
#property (nonatomic, strong) Protocol *protocol;
- (void)addImplementation:(id)blockImplementation forSelector:(SEL)selector;
#end
#implementation EJHDelegateObject
static NSInteger counter;
+ (id)delegateObjectForProtocol:(Protocol *)protocol
{
NSString *className = [NSString stringWithFormat:#"%s%#%i",protocol_getName(protocol),#"_EJH_implementation_", counter++];
Class protocolClass = objc_allocateClassPair([EJHDelegateObject class], [className cStringUsingEncoding:NSUTF8StringEncoding], 0);
class_addProtocol(protocolClass, protocol);
objc_registerClassPair(protocolClass);
EJHDelegateObject *object = [[protocolClass alloc] init];
object.protocol = protocol;
return object;
}
- (void)addImplementation:(id)blockImplementation forSelector:(SEL)selector
{
unsigned int outCount;
struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(self.protocol, NO, YES, &outCount);
struct objc_method_description description;
BOOL descriptionFound = NO;
for (int i = 0; i < outCount; i++){
description = methodDescriptions[i];
if (description.name == selector){
descriptionFound = YES;
break;
}
}
if (descriptionFound){
class_addMethod([self class], selector, imp_implementationWithBlock(blockImplementation), description.types);
}
}
#end
And using an EJHDelegateObject:
self.alertViewDelegate = [EJHDelegateObject delegateObjectForProtocol:#protocol(UIAlertViewDelegate)];
[self.alertViewDelegate addImplementation:^(id _self, UIAlertView* alertView, NSInteger buttonIndex){
NSLog(#"%# dismissed with index %i", alertView, buttonIndex);
} forSelector:#selector(alertView:didDismissWithButtonIndex:)];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Example" message:#"My delegate is an EJHDelegateObject" delegate:self.alertViewDelegate cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
[alertView show];
Edit: This is what I've come up after having understood your requirement. This is just a quick hack, an idea to get you started, it's not properly implemented, nor is it tested. It is supposed to work for delegate methods that take the sender as their only argument. It works It is supposed to work with normal and struct-returning delegate methods.
typedef void *(^UBDCallback)(id);
typedef void(^UBDCallbackStret)(void *, id);
void *UBDDelegateMethod(UniversalBlockDelegate *self, SEL _cmd, id sender)
{
UBDCallback cb = [self blockForSelector:_cmd];
return cb(sender);
}
void UBDelegateMethodStret(void *retadrr, UniversalBlockDelegate *self, SEL _cmd, id sender)
{
UBDCallbackStret cb = [self blockForSelector:_cmd];
cb(retaddr, sender);
}
#interface UniversalBlockDelegate: NSObject
- (BOOL)addDelegateSelector:(SEL)sel isStret:(BOOL)stret methodSignature:(const char *)mSig block:(id)block;
#end
#implementation UniversalBlockDelegate {
SEL selectors[128];
id blocks[128];
int count;
}
- (id)blockForSelector:(SEL)sel
{
int idx = -1;
for (int i = 0; i < count; i++) {
if (selectors[i] == sel) {
return blocks[i];
}
}
return nil;
}
- (void)dealloc
{
for (int i = 0; i < count; i++) {
[blocks[i] release];
}
[super dealloc];
}
- (BOOL)addDelegateSelector:(SEL)sel isStret:(BOOL)stret methodSignature:(const char *)mSig block:(id)block
{
if (count >= 128) return NO;
selectors[count] = sel;
blocks[count++] = [block copy];
class_addMethod(self.class, sel, (IMP)(stret ? UBDDelegateMethodStret : UBDDelegateMethod), mSig);
return YES;
}
#end
Usage:
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
UniversalBlockDelegate *d = [[UniversalBlockDelegate alloc] init];
webView.delegate = d;
[d addDelegateSelector:#selector(webViewDidFinishLoading:) isStret:NO methodSignature:"v#:#" block:^(id webView) {
NSLog(#"Web View '%#' finished loading!", webView);
}];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com"]]];