iOS NSObject nil after initialisation in block - objective-c

I have an NSObject that I create inside a block. As per the code below:
__block NSObject *myObject;
[self myMethod:^{
myObject = [[NSObject alloc] init];
....
}];
if(myObject == nil){
NSLog(#"Why is my object nil?!");
}
In the definition of myMethod I have the following:
backgroundQueue = dispatch_queue_create("backgroundqueue", NULL);
dispatch_async(backgroundQueue,
^{
dispatch_async(dispatch_get_main_queue(),
^{
if(block){
block();//Never called.
}
});
However the block is never called.

The problem here is that you never seem to execute the block in which you instantiate myObject. For illustration, run this little program:
#import <Foundation/Foundation.h>
typedef void(^MyTestBlock)(void);
#interface Foo:NSObject
- (id)initWithBlock:(MyTestBlock)aBlock;
- (void)someMethod;
#end
#implementation Foo {
MyTestBlock _block;
}
- (id)initWithBlock:(MyTestBlock)aBlock {
self = [super init];
if( !self ) { return nil; }
_block = aBlock;
return self;
}
- (void)someMethod {
_block();
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
__block NSObject *myObject;
Foo *myFoo = [[Foo alloc] initWithBlock:^{
myObject = [[NSObject alloc] init];
}];
[myFoo someMethod];
NSLog((myObject)?#"Your object was created":#"Why is my object nil?");
}
}
This prints 2012-11-26 05:00:58.519 Untitled 2[23467:707] Your object was created to the console. The point is that blocks don't execute themselves. In the code above, although we set the block as an ivar of the class, we don't execute it until we call someMethod on our Foo.
EDIT:
An edit to your question states that the block is not executed in the context of an asynchronous dispatch block sent to the main queue. If this is a command line application, then you must call dispatch_main() at the end of main. See the man page for dispatch_get_main_queue(). Here is a full working command line application to illustrate this, as well as issues related to race conditions:
#import <Foundation/Foundation.h>
typedef void(^MyTestBlock)(void);
#interface Foo:NSObject
- (id)initWithBlock:(MyTestBlock)aBlock;
- (void)someMethod;
#end
#implementation Foo {
MyTestBlock _block;
}
- (id)initWithBlock:(MyTestBlock)aBlock {
self = [super init];
if( !self ) { return nil; }
_block = aBlock;
return self;
}
- (void)someMethod {
dispatch_queue_t backgroundQueue = dispatch_queue_create("backgroundqueue", NULL);
dispatch_async(backgroundQueue, ^{
dispatch_queue_t innerQueue = dispatch_get_main_queue();
dispatch_async(innerQueue, ^{
if( _block){
NSLog(#"Will call block.");
_block();
}
});
});
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
__block NSObject *myObject;
Foo *myFoo = [[Foo alloc] initWithBlock:^{
myObject = [[NSObject alloc] init];
}];
[myFoo someMethod];
// this log statement should show that myObject is nil because it will (probably)
// be executed before your block.
NSLog((myObject)?#"Your object was created":#"Why is my object nil?");
// wait a little bit to resolve race condition (just for illustrative purposes)
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.4f * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog((myObject)?#"Your object was created":#"Why is my object nil?");
});
}
// this isn't a Cocoa app, so must call dispatch_main() at end of main
dispatch_main();
}

You have forgotten to call your block in your myMethod. Try the code bellow.
typedef void(^MyBlock)();
- (void)myMethod:(MyBlock)aBlock
{
aBlock();
}

Related

iOS(Objective-C) Crashed for EXC_BAD_ACCESS,when get and set a atomic Global variable in Multithreading

My app crashed, suspected to be caused by multi-threaded operation of an attribute in a singleton object.
So I wrote a small piece of code and successfully reproduced the problem, but I still couldn't understand it.
I have defined the property as #property, which is atomic. Why does it still crash when accessed by multiple threads? Below is my code snippet:
Audio.h
#interface Audio : NSObject
#property NSString *audioName;
#property NSString *audioData;
#end
Audio.m
#import "Audio.h"
#implementation Audio
- (instancetype)init{
self = [super init];
if (self) {
_audioData = #"";
_audioName = nil;
}
return self;
}
#end
AudioManager.h
#interface AudioManager : NSObject
+(instancetype)shareInstance;
#property Audio *curAudio;
-(void) play;
-(void) clearCurAudio;
#end
AudioManager.m
#import "AudioManager.h"
#implementation AudioManager
static id sharedInstance = nil;
+(instancetype)shareInstance {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(void) play {
NSLog(#"Current Audio name : %#",_curAudio.audioName);
NSLog(#"Current Audio name : %#",_curAudio.audioData);
NSLog(#"Current Audio name : %#",_curAudio.audioName);//crahed here!
NSLog(#"Current Audio name : %#",_curAudio.audioData);
}
-(void) clearCurAudio {
_curAudio = nil;
}
#end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
dispatch_queue_t thread1 = dispatch_queue_create("queue1", nil);
dispatch_queue_t thread2 = dispatch_queue_create("queue2", nil);
dispatch_queue_t thread3 = dispatch_queue_create("queue3", nil);
dispatch_async(thread1, ^{
for (int i = 0; i < 1000; i++) {
Audio *newAudio = [[Audio alloc] init];
newAudio.audioName = #"na";
[[AudioManager shareInstance] setCurAudio:newAudio];
}
});
//
dispatch_async(thread2, ^{
for (int i = 0; i < 1000; i++) {
[[AudioManager shareInstance] play];
}
});
//
dispatch_async(thread3, ^{
for (int i = 0; i < 1000; i++) {
AudioManager * audioManager = [AudioManager shareInstance];
[[AudioManager shareInstance] clearCurAudio];
}
});
}
Here is the crash EXC_BAD_ACCESS:
enter image description here
Thank you guys,problem solved!!, as #Willeke posted.
use self.curAudio rather than _curAudio.

Wrapping a block with a block

I have a method that takes a block as an argument. That block needs to be augmented and then passed to a library function that block as an argument. An example:
typedef void (^eblock_t)(void);
void libraryFunction(eblock_t block);
- (void)myMethod:(eblock_t)block {
libraryFunction ( ^{
block();
NSLog(#"block executed"); // This is the augmentation of the block
} );
}
That example is pretty straight forward and works for straight forward situations. I evolved that example a bit to the following using GHUnit. It is a bit contrived, but works to illustrate my problem as concisely as possible:
EBlock.h
typedef void (^eblock_t)(void);
#interface EBlock : NSObject {
eblock_t _block;
}
#property (nonatomic, readwrite, strong) eblock_t blockOption1;
#property (nonatomic, readwrite, strong) eblock_t blockOption2;
- (void)chooseBlock:(NSUInteger)option;
- (void)executeBlock;
#end
EBlock.m
#import "EBlock.h"
#implementation EBlock
- (void)chooseBlock:(NSUInteger)option {
if (1 == option) {
// This is a block wrapping a block to augment the block
// This is the source of problem with test_switchOption_1For2
_block = ^{
self.blockOption1();
NSLog(#"option1"); // This is the augmentation
};
} else {
// There is no block wrapping the block and thus no augmentation of the block
// There is no issue with test_switchOption_2For1
_block = self.blockOption2;
}
}
- (void)executeBlock { _block(); }
#end
Test_EBlock.h
#class EBlock;
#interface Test_EBlock : GHTestCase
#property (nonatomic, readonly) NSUInteger counter1;
#property (nonatomic, readonly) NSUInteger counter2;
- (void)incrementCounter1;
- (void)incrementCounter2;
#end
Test_EBlock.m
#import "Test_EBlock.h"
#import "EBlock.h"
#implementation Test_EBlock
- (void)incrementCounter1 { _counter1++; }
- (void)incrementCounter2 { _counter2++; }
- (void)setUp {
[super setUp];
_counter1 = _counter2 = 0u;
}
- (void)tearDown { [super tearDown]; }
- (void)test_option1 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:1];
[foo executeBlock];
GHAssertEquals(self.counter1, 1u, nil);
GHAssertEquals(self.counter2, 0u, nil);
}
- (void)test_option2 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:2];
[foo executeBlock];
GHAssertEquals(self.counter1, 0u, nil);
GHAssertEquals(self.counter2, 1u, nil);
}
- (void)test_switchOption_1For2 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:1];
// switch what is done in the block
foo.blockOption1 = ^{ [self incrementCounter2]; };
[foo executeBlock];
GHAssertEquals(self.counter1, 1u, nil); // This fails
GHAssertEquals(self.counter2, 0u, nil); // This fails
}
- (void)test_switchOption_2For1 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:2];
// switch what is done in the block
foo.blockOption2 = ^{ [self incrementCounter1]; };
[foo executeBlock];
GHAssertEquals(self.counter1, 0u, nil);
GHAssertEquals(self.counter2, 1u, nil);
}
Discussion
Test: test_option1, test_option2, & test_switchOption_2For1 pass.
test_switchOption_1For2 fails because of GHAssertEquals(self.counter1, 0u, nil); and GHAssertEquals(self.counter2, 1u, nil);
This is because the block that is being executed self.blockOption1 is actually [self incrementCounter2] and not [self incrementCounter1]. This is because in EBlock.m chooseBlock the block wrapping the block has copied self.blockOption1 which at the time of evaluation is [self incrementCounter2]. Is there a better way to augment the block so the block does not have to be wrapped? Or is there a way not to delay the evaluation of self.blockOption1 so that it is [self incrementCounter1].
What is captured by your wrapping block is self, not the value of self.blockOption1. If you want to capture the latter, try:
- (void)chooseBlock:(NSUInteger)option {
if (1 == option) {
eblock_t local_block = self.blockOption1;
// This is a block wrapping a block to augment the block
_block = ^{
local_block();
NSLog(#"option1"); // This is the augmentation
};
} else {
// There is no block wrapping the block and thus no augmentation of the block
// There is no issue with test_switchOption_2For1
_block = self.blockOption2;
}
}
If I understand you correctly you wish to delay the effect of chooseBlock until executeBlock to that the block can be changed between them. Just rearrange your logic (typed directly into SO, could be tided up):
#import "EBlock.h"
#implementation EBlock
{
NSUInteger currentChoice;
}
- (void)chooseBlock:(NSUInteger)option
{
currentChoice = option;
}
- (void)executeBlock
{
if (1 == option)
{ // This is a block wrapping a block to augment the block
// This is the source of problem with test_switchOption_1For2
_block = ^{
self.blockOption1();
NSLog(#"option1"); // This is the augmentation
};
}
else
{
// There is no block wrapping the block and thus no augmentation of the block
// There is no issue with test_switchOption_2For1
_block = self.blockOption2;
}
_block();
}
#end
You may want to copy the block that came in as a argument/property. Try go with:
#property (copy) eblock_t block;

NSCondition ->Objective C

I am a newbie to Objective-C. I'm currently working on threads.
I have to make a synchronous execution of threads. I'm using NSInvocationOperaion to spawn a thread.
I have two threads. I need to wait for the 1st thread to signal a event or the timeout.
Signalling a event can be done by NSConditionLock. How to signal a timeout. I could not use waitUntilDate method here as the timeout is not a fixed value.
Is there any way to do this?
EDITED
main.m
------
#import "PseudoSerialQueue.h"
#import "PseudoTask.h"
int main()
{
PseudoSerialQueue* q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test2)];
[q quit];
return 0;
}
PseudoTask.h
-----------------
#import <Foundation/Foundation.h>
#interface PseudoTask : NSObject {
id target_;
SEL selector_;
id queue_;
}
#property(nonatomic,readonly)id target;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
-(void)exec;
#end
PseudoTask.m
-----------------
#import "PseudoTask.h"
#implementation PseudoTask
#synthesize target = target_;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue
{
self = [super init];
if (self) {
target_ = [target retain];
selector_ = selector;
queue_ = [queue retain];
}
return self;
}
-(void)exec
{
[target_ performSelector:selector_];
}
-(void)dealloc
{
[super dealloc];
[target_ release];
[queue_ release];
}
#end
PseudoSerialQueue.h
----------------------------
#import <Foundation/Foundation.h>
#import "PseudoTask.h"
#interface PseudoSerialQueue : NSObject {
NSCondition* condition_;
NSMutableArray* array_;
NSThread* thread_;
}
-(void)addTask:(id)target selector:(SEL)selector;
#end
PseudoSerialQueue.m
----------------------------
#import "PseudoSerialQueue.h"
#implementation PseudoSerialQueue
-(id)init
{
self = [super init];
if (self) {
array_ = [[NSMutableArray alloc]init];
condition_ = [[NSCondition alloc]init];
thread_ = [[NSThread alloc] initWithTarget:self selector:#selector(execQueue) object:nil];
[thread_ start];
}
return self;
}
-(void)addTask:(id)target selector:(SEL)selector
{
[condition_ lock];
PseudoTask* task = [[PseudoTask alloc] initWithTarget:target selector:selector queue:self];
[array_ addObject:task];
[condition_ signal];
[condition_ unlock];
}
-(void)quit
{
[self addTask:nil selector:nil];
}
-(void)execQueue
{
for(;;)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
[condition_ lock];
if (array_.count == 0) {
[condition_ wait];
}
PseudoTask* task = [array_ objectAtIndex:0];
[array_ removeObjectAtIndex:0];
[condition_ unlock];
if (!task.target) {
[pool drain];
break;
}
[task exec];
[task release];
[pool drain];
}
}
-(void)dealloc
{
[array_ release];
[condition_ release];
[super dealloc];
}
#end
I could not pass self from main.Hope i'm mistakenly calling it.
Error:'self' undeclared is coming.
I could not understand
-(void)exec
{
[target_ performSelector:selector_];
}
in PseudoTask.m
target_ is not a method and its an ivar.
I am not getting any error or warning.But i could not understand that code.
I am writing what i have understood from your program.Please correct me if i my way of understanding the program is wrong.
The Thread execQueue is spawned when the PseudoSerialQueue is initialised and it waits for the signal from the addTask method.
The addTask method is called in the quit method and the parameters passed are nil.I could not understand why to pass a nil parameter.
It would be helpful if you explain about it.Thanks.
You mean NSCondition? You can use waitUntilDate: as relative time.
[condition lock];
// wait 5 seconds.
[condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
[condition unlock];
EDITED:
My PseudoSerialQueue class requires to be called from a class that is derived from NSObject like the following.
#interface Test : NSObject
#end
#implementation Test
- (void)test0
{
}
- (void)test1
{
}
- (id)init
{
self = [super init];
return self;
}
- (void)exec
{
PseudoSerialQueue *q = [[PseudoSerialQueue alloc] init];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test0)];
[q quit];
}
#end
You can call it from main function.
Test *test = [[Test alloc] init];
[test exec];
I could not understand why to pass a nil parameter.
I just only chose it for the message of quitting the loop in the PseudoSerialQueue.
Let the 1st thread signal the 2nd one in both cases; then in the second thread you can tell in which case you are based on some read-only flag in the 1st controller or in your model (say, isDataAvailable).

asynchronous call using the NSOperationQueue

I am a beginner in developing iPhone applications.
I was doing this sample program below and got an error- invalid use of void expression
threadsss.h
------------
#import <Foundation/Foundation.h>
#interface threadsss : NSObject {
BOOL m_bRunThread;
int a,b,c;
}
-(void)Thread;
-(void)add;
-(void)display;
#end
threadsss.m
------------
#import "threadsss.h"
#implementation threadsss
-(void)Thread
{
m_bRunThread = YES;
NSOperationQueue* queue = [[NSOperationQueue alloc]init];
NSInvocationOperation* operation = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(display) object:nil];
[operation addDependency:[self add]];
[queue addOperation:operation];
[queue release];
}
-(void)add
{
NSLog(#"Going to add a and b!!");
a=1;
b=2;
c = a + b;
NSLog(#"Finished adding!!");
}
-(void)display
{
NSLog(#"Into the display method");
NSLog(#"The value od c is:%d",c);
}
#end
main.m
-------
#import <Foundation/Foundation.h>
#import "threadsss.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
threadsss* thread = [[threadss alloc]init];
[thread Thread];
[pool drain];
return 0;
}
I want to make an asynchronous call between the add and the display methods.After calling the display method i want to execute the add method. and in the meanwhile after printing the "I'm into the display method" the display method will wait for the add to perform its operation and the add after doing its operation will notify its completion to the display method.The display method will then print the result c.
I have tried to implement it with that in my mind.Do i need to do any other modification in my program or is the way i have implemented through dependecies is correct.
EDITED
threadss.h
-----------
#import <Foundation/Foundation.h>
#interface threadss : NSObject {
BOOL m_bRunThread;
int a,b,c;
NSOperationQueue* queue;
NSInvocationOperation* operation;
NSInvocationOperation* operation1;
NSConditionLock* theConditionLock;
}
-(void)Thread;
-(void)add;
-(void)display;
#end
threadss.m
-----------
#import "threadss.h"
#implementation threadss
-(id)init
{
if (self = [super init]) {
queue = [[NSOperationQueue alloc]init];
operation = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(display) object:nil];
operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(add) object:nil];
theConditionLock = [[NSConditionLock alloc]init];
}
return self;
}
-(void)Thread
{
m_bRunThread = YES;
//[operation addDependency:operation1];
if (m_bRunThread) {
[queue addOperation:operation];
}
//[operation addDependency:operation1];
[queue addOperation:operation1];
//[self performSelectorOnMainThread:#selector(display) withObject:nil waitUntilDone:YES];
//NSLog(#"I'm going to do the asynchronous communication btwn the threads!!");
//[self add];
//[operation addDependency:self];
sleep(1);
[queue release];
[operation release];
//[operation1 release];
}
-(void)add
{
NSLog(#"Going to add a and b!!");
a=1;
b=2;
c = a + b;
NSLog(#"Finished adding!!");
}
-(void)display
{
NSLog(#"Into the display method");
[operation1 waitUntilFinished];
NSLog(#"The Result is:%d",c);
}
#end
main.m
------
#import <Foundation/Foundation.h>
#import "threadss.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
threadss* thread = [[threadss alloc]init];
[thread Thread];
[pool drain];
return 0;
}
I made two operation queues.
But using waitUntilFinished on the same queue may lead to deadlock.How do i do the wait in display method for the add operation to complete its execution.
First, it would be easier to answer your question if you identified the line than the compiler was complaining about and showing only the relevant code. However, in this case it's pretty straight-forward. It's this line:
[operation addDependency:[self add]];
The add method returns nothing (void). And you're telling operation to add that as a dependency. What does that mean?
Either, you should change add to return self or call add on the line before:
[self add];
[operation addDependency:self];

Objective C - Unit testing & Mocking object?

- (BOOL)coolMethod:(NSString*)str
{
//do some stuff
Webservice *ws = [[WebService alloc] init];
NSString *result = [ws startSynchronous:url];
if ([result isEqual:#"Something"])
{
//More calculation
return YES;
}
return NO;
}
I am using OCUnit
In the following method how can i mock my WebService Object, or the result to the method "startSynchronous" to be able to write an independent unit test?
Is it possible to inject some code in there to either create a mock web service or return a mock data on startSynchronous call?
One way is to use categories and override methods you want, you can even override the init method to return a mock object:
#interface Webservice (Mock)
- (id)init;
#end
#implementation Webservice (Mock)
- (id)init
{
//WebServiceMock is a subclass of WebService
WebServiceMock *moc = [[WebServiceMock alloc] init];
return (Webservice*)moc;
}
#end
The problem with this is that if you want to make the object return different results in different tests in 1 test file you cannot do that. (You can override each method once per test page)
EDIT:
This is an old question I posted, I thought I would update the answer to how I write testable code and unit test it nowadays :)
ViewController Code
#implementation MyViewController
#synthesize webService;
- (void)viewDidLoad
{
[super viewDidLoad];
[self.webService sendSomeMessage:#"Some_Message"];
}
- (WebService *)webService
{
if (!_webService)
_webService = [[WebService alloc] init];
return _webService;
}
#end
Test Code
#implementation MyViewControllerTest
- (void)testCorrectMessageIsSentToServer
{
MyViewController *vc = [[MyViewController alloc] init];
vc.webService = [OCMock niceMockForClass:[WebService class]];
[[(OCMockObject *)vc.webService expect] sendSomeMessage#"Some_Message"];
[vc view]; /* triggers viewDidLoad */
[[(OCMockObject *)vc.webService verify];
}
#end
Building on top of the WebService answer from aryaxt, here's a little trick to be able to get different results in different test.
First, you need a singleton object which will be used to store the desired answer, right before the test
TestConfiguration.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
void MethodSwizzle(Class c, SEL orig, SEL new);
#interface TestConfiguration : NSObject
#property(nonatomic,strong) NSMutableDictionary *results;
+ (TestConfiguration *)sharedInstance;
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector;
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector;
#end
TestConfiguration.m
#import "TestConfiguration.h"
void MethodSwizzle(Class c, SEL orig, SEL new) {
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
};
#implementation TestConfiguration
- (id)init
{
self = [super init];
if (self) {
self.results = [[NSMutableDictionary alloc] init];
}
return self;
}
+ (TestConfiguration *)sharedInstance
{
static TestConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[TestConfiguration alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)setNextResult:(NSObject *)result
forCallToObject:(NSObject *)object
selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
[self.results setObject:result
forKey:[[className stringByAppendingString:#":"] stringByAppendingString:selectorName]];
}
-(NSObject *)getResultForCallToObject:(NSObject *)object selector:(SEL)selector
{
NSString *className = NSStringFromClass([object class]);
NSString *selectorName = NSStringFromSelector(selector);
return [self.results objectForKey:[[className stringByAppendingString:#":"] stringByAppendingString:selectorName]];
}
#end
Then you would define your "Mock" category to define mock methods , such as :
#import "MyWebService+Mock.h"
#import "TestConfiguration.h"
#implementation MyWebService (Mock)
-(void)mockFetchEntityWithId:(NSNumber *)entityId
success:(void (^)(Entity *entity))success
failure:(void (^)(NSError *error))failure
{
Entity *response = (Entity *)[[TestConfiguration sharedInstance] getResultForCallToObject:self selector:#selector(fetchEntityWithId:success:failure:)];
if (response == nil)
{
failure([NSError errorWithDomain:#"entity not found" code:1 userInfo:nil]);
}
else{
success(response);
}
}
#end
And finally, in the tests themselves, you would swizzle the mock method in the setup , and define the expected answer in each test, before the call
MyServiceTest.m
- (void)setUp
{
[super setUp];
//swizzle webservice method call to mock object call
MethodSwizzle([MyWebService class], #selector(fetchEntityWithId:success:failure:), #selector(mockFetchEntityWithId:success:failure:));
}
- (void)testWSMockedEntity
{
/* mock an entity response from the server */
[[TestConfiguration sharedInstance] setNextResult:[Entity entityWithId:1]
forCallToObject:[MyWebService sharedInstance]
selector:#selector(fetchEntityWithId:success:failure:)];
// now perform the call. You should be able to call STAssert in the blocks directly, since the success/error block should now be called completely synchronously.
}
Remark : in my example, the TestConfiguration uses class/selector as a key instead of object/selector. That means every object of the class will use the same answer for the selector. That is most likely your case, as webservice are often singleton. But it should be improved to an object/selector maybe using the objet's memory address instead of its class