How to properly stub methods on a different thread using OCMock? - objective-c

I have the following unit test that is failing. I think its because OCMock does not work well across multiple threads but I could be wrong. mockTestMethodA never gets called. If I modified the code to call the testMethodA on the same thread (without NSThread), stubbing seems to work. Is this a known limitation of OCMock or am I missing something?
Sample code:
- (void) testCallMethodUsingNSThreadFromADifferentClass
{
mockTestClassA = [OCMockObject partialMockForObject:testClassA];
[[[mockTestClassA expect] andCall:#selector(mockTestMethodA) onObject:self] testMethodA];
[testClassC threadedRequestToCallMethodA];
[self waitForCompletion:5.0];
[mockTestClassA verify];
}
threadedRequestToCallMethodA and callMethodAFromTestClassC in TestClassC are defined as follows:
- (void) threadedRequestToCallMethodA
{
[NSThread detachNewThreadSelector:#selector(callMethodAFromTestClassC) toTarget:self withObject:nil];
}
- (void) callMethodAFromTestClassC
{
[[[TestClassA alloc] init] testMethodA];
}
testMethodA in TestClassA is defined as:
- (void) testMethodA
{
NSLog(#"testMethodA");
}
Stub method is defined as follows:
- (void) mockTestMethodA
{
NSLog(#"mockTestMethodA");
}
And finally waitForCompletion:
- (BOOL) waitForCompletion:(NSTimeInterval)timeoutSecs
{
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if([timeoutDate timeIntervalSinceNow] < 0.0)
break;
} while (!done);
return done;
}
Your help is appreciated.
Thanks.

Your issue is that you aren't calling the test method on the object you partially mocked, but are instead alloc'ing a new one in your callMethodAFromTestClassC. If you call it on the proper object, it works fine. See below (note I don't actually create a separate class C, but the effect is the same). As a side note, I think blocks and GCD make life easier, but everyone has their own style.
//
// TestClassTest.m
// TestApp
//
// Created by Benjamin Flynn on 11/20/12.
//
#import <SenTestingKit/SenTestingKit.h>
#import "OCMock.h"
#import "TestClassA.h"
#interface TestClassTest : SenTestCase
#property (nonatomic, retain) TestClassA *testClassA;
#property (atomic, assign) BOOL done;
#end
#implementation TestClassTest
- (void) testCallMethodUsingNSThreadFromADifferentClass
{
self.testClassA = [[TestClassA alloc] init];
id mockTestClassA = [OCMockObject partialMockForObject:self.testClassA];
[[[mockTestClassA expect] andCall:#selector(mockTestMethodA) onObject:self] testMethodA];
[self threadedRequestToCallMethodA];
[self waitForCompletion:5.0];
[mockTestClassA verify];
}
- (void)threadedRequestToCallMethodA
{
[NSThread detachNewThreadSelector:#selector(callMethodAFromTestClassC) toTarget:self withObject:nil];
}
- (void)callMethodAFromTestClassC
{
[self.testClassA testMethodA];
}
- (void)mockTestMethodA
{
NSLog(#"Mock test method A");
self.done = YES;
}
- (BOOL)waitForCompletion:(NSTimeInterval)timeoutSecs
{
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
NSLog(#"Starting timer");
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if([timeoutDate timeIntervalSinceNow] < 0.0)
break;
} while (!self.done);
NSLog(#"Ending timer");
return self.done;
}
#end

Related

How to swizzle initialization method?

I have one class MyOldController with init method
-(instancetype) initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
I want swizzle this initialization method to another and this my swizzle code
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithMyController)), class_getInstanceMethod(self, #selector(swizzle_ initWithMyController)));
}
I try write this
-(instancetype) swizzle_initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
But it drops error
Then I renamed init method to this and updated (void)swizzleMethods
-(instancetype) initWithMyController_swizzle: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
Error message disappeared but swizzle doesn't works. It just calls old initialization method, not my new.
Which point i missed? Is swizzling of initialization method have some special way to do it?
(Starting with the required caveat: this is incredibly dangerous and should never be used in production code. Swizzling initializers is particularly dangerous given designated initializer chaining, and should definitely never be done for anything but exploration and debugging without first confirming the implementation of the swizzled initializer. OK, got that out of the way.)
I can't reproduce your issue. And initializer should always start with with init, so your second approach is correct. I suspect you've just made a small mistake, perhaps in your #selector (which has a typo in your question, which suggests maybe there's a mistake in your actual code). Here is code that does what you're describing.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface MyOldController: NSObject
- (instancetype)initWithInt:(NSInteger)x
#end
#implementation MyOldController
- (instancetype)initWithInt:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init");
}
return self;
}
#end
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithInt:)), class_getInstanceMethod(self, #selector(initWithInt_swizzle:)));
}
- (instancetype)initWithInt_swizzle:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init_swizzle");
}
return self;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyOldController *controller = [[MyOldController alloc] initWithInt:1];
NSLog(#"%#", controller);
}
return 0;
}
This prints, as expected:
2018-06-21 12:23:14.431936-0400 test[30981:401466] init_swizzle
2018-06-21 12:23:14.432172-0400 test[30981:401466] <MyOldController: 0x10051ee10>

Method doesn't run when invoked

EDIT: Problem has been solved, I moved allocation and initiation of variables into another method. SubOtherClass never gets initiated (alloc and init are never called).
Classes have been renamed to make this question more general.
Hypothetical class OtherClass extends NSView
Hypothetical class SubOtherClass extends the hypothetical class OtherClass and invokes the update method in a local instance of ClassToUpdate
I understand that updating the view when a key gets released is not the best of ideas, but that's only temporary. I'm not an expert in Obj-C. To repeat the problem, the update method in SubOtherClass gets executed but not in ClassToUpdate, and the content (not shown here) of that method doesn't run. How can I fix this? If anymore info is necessary, just ask.
Thanks.
Edit: Full code (with renamed classes)
Header:
#import "OtherClass.h"
#import "ThingToRender.h"
#import "ClassToUpdate.h"
#interface SubOtherClass : OtherClass
#property (assign) ThingToRender *thingToRender1, *thingToRender2;
#property (retain) ClassToUpdate *classToUpdate;
- (void) createVariables;
- (void) update;
#end
Implementation:
#import "SubOtherClass.h"
#implementation SubOtherClass
- (BOOL) acceptsFirstResponder{
return true;
}
- (void) createVariables{
self.classToUpdate = [[ClassToUpdate alloc] init];
self.thingToRender1 = [[ThingToRender alloc] init];
self.thingToRender2 = [[ThingToRender alloc] init];
}
- (void) keyUp:(NSEvent *)theEvent{
[super keyUp:theEvent];
[self setNeedsDisplay:true];
}
- (void) keyDown:(NSEvent *)theEvent{
[super keyDown:theEvent];
}
- (void) update{
[self.classToUpdate update:self];
}
- (void) drawRect:(NSRect)rect{
[super drawRect:rect];
[self update];
[[NSColor blackColor] set];
NSRectFill(rect);
[self.color1 set]; //this color is declared in superclass
NSString *str1 = [NSString stringWithFormat:#"%d %d %d %d", self.thingToRender1.x, self.thingToRender1.y, 30, 100];
NSRectFill(NSRectFromString(str1));
[self.color2 set]; //this color is declared in superclass
str1 = [NSString stringWithFormat:#"%d %d %d %d", self.thingToRender2.x, self.thingToRender2.y, 30, 100];
NSRectFill(NSRectFromString(str1));
[self.color3 set]; //this color is declared in superclass
str1 = [NSString stringWithFormat:#"%d %d %d %d", self.classToUpdate.x, self.classToUpdate.y, 30, 30];
NSRectFill(NSRectFromString(str1));
}
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
[self setNeedsDisplay:YES];
return self;
}
#end
OtherClass extends NSView
Are you sure that self.classToUpdate isn't nil while executing?
Maybe you're not initializing that class anywhere?
Replace update method in SubOtherClass with this code:
- (void) update{
if(!self.classToUpdate){
NSLog(#"classToUpdate is nil");
}
[self.classToUpdate update:self];
}
And look on the console if 'classToUpdate is nil' text appears
In your drawrect method just include this line:-
[super drawrect:rect]
So that it call super class drawrect method
I'm not exactly sure what was causing the problem, and I find it complicated, but I eventually found the solution. I moved constructors for the variables to another method, and alloc is never called on SubOtherClass. Thanks anyway for everyone's help.

Objective C - deallocated variable

I have this static/singelton class that has a member variable.
Everything works just fine, however, when I quit the app, and the come back, that member variable is causing the app to crash.
I tried reinitializing the member variable, but doesn't seem to do the trick.
I am including samples of my code below, has anyone seen this before?
The header (.h) file:
#interface Metrics : NSObject {
Metrics *metrics;
Distance *distance;
}
The implementation (.m) file:
#implementation Metrics
static Metrics *metrics = nil;
+ (Metrics *)sharedInstance {
#synchronized(self) {
if (metrics == nil) {
metrics = [[Metrics alloc] init];
}
}
return metrics;
}
- (id)init {
self = [super init];
if (self) {
}
return self;
}
-(void) setupDistance
{
distance = [[Distance alloc] init];
distance.test; // it dies here after I quit the app and come back
}
And where it is used, AppDelegate.m
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[Metrics sharedInstance] setupDistance];
}
Thank you,
Tee
When you say "quit the app" you almost certainly mean "send the app to the background." This does not terminate your application. When you become active again, you're leaking distance and assigning a new one. I suspect that something in test is actually the problem.
First, don't access your ivars directly, except in init and dealloc. Use accessors. This will make most of your problems go away.
Second, don't call setupDistance in applicationDidBecomeActive. You mean to call it in applicationDidFinishLaunchingWithOptions:. But better yet, just initialize it during Metrics init. Why have this internal Metrics detail in the app delegate?
applicationDidBecomeActive will get called when it comes back from sleep calling to setup in the distance again. It's wierd that you're calling a property (.test) and not assigning. Is test doing work? Should it be a method? What's it doing?
Also, in a singleton you have to overload quite a few things. Here's a good template for a singleton that I use.
static MySingleton *sharedInstance = nil;
#implementation MySingleton
#pragma mark -
#pragma mark class instance methods
#pragma mark -
#pragma mark Singleton methods
+ (MySingleton*)sharedInstance
{
#synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [super allocWithZone:zone];
return sharedInstance;  // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;  // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
#end

Singleton not initializing correctly

I have the following code that I am calling using this statement: SQLiteDB *db = [[[SQLiteDB alloc] init] autorelease];
The problem is "sharedSQLiteDB" is not being called, but rather "allocWithZone" is, and therefore "checkIfDatabaseExists" is not being called, which is where the database is created.
I don't understand why... (i.e. what am I doing wrong?)
#import "SQLiteDB.h"
static SQLiteDB *sharedSQLiteDB = nil; // makes this a singleton class
#implementation SQLiteDB
#synthesize searchPaths, documentPath, databasePath, cDatabasePath;
#pragma mark Singleton Methods
+ (SQLiteDB *) sharedSQLiteDB {
if(!sharedSQLiteDB) {
sharedSQLiteDB = [[SQLiteDB alloc] init];
[sharedSQLiteDB checkIfDatabaseExists]; // check to see if d/b exists
}
return sharedSQLiteDB;
}
+(id)allocWithZone:(NSZone *)zone { // makes sure another instance is not allocated
if(!sharedSQLiteDB) {
sharedSQLiteDB = [super allocWithZone:zone];
return sharedSQLiteDB;
}
else {
return nil;
}
}
-(id)copyWithZone:(NSZone *)zone {
return self;
}
-(void) release {
// no-op
}
In the singleton pattern your use pattern should be:
SQLiteDB* db = [SQLiteDB sharedSQLiteDB];
They way you are calling it doesn't fit the singelton pattern. All access should be through your sharedSQLiteDB message.
In other words you shouldn't be initializing via typical Cocoa patterns (SQLiteDB *db = [[[SQLiteDB alloc] init] autorelease]; is incorrect and full of problems) outside the scope of the class.
In a singleton using the default initialization pattern for the language (alloc/init for ObjC or the default constructor for C++) should generate a compile time error message since the constructor/init method should be protected.
See the Wikipedia entry. consult the Design Pattern C++ bible. There is even a version for Cocoa
Good luck.
It isn't executing your + (SQLiteDB *) sharedSQLiteDB method because you're not actually calling that method anywhere.
As you've seen, when you call [[SQLiteDB alloc] init], the allocWithZone method is called.
Change your call to be SQLiteDB *db = [SQLiteDB sharedSQLiteDB], which will call your checkIfDatabaseExists method in this case. However, if [[SQLiteDB alloc] init] is called somewhere else, then the checkIfDatabaseExists method call will still be skipped.
Maybe consider moving the checkIfDatabaseExists method into an init method so that it will be called for both your singleton method and your allocWithZone.
Honestly I don't see any error...
However I post the code I used to create a Singleton. It's from a source that now I don't remember the link... it's not my code.
static DataManager *_instance;
#implementation DataManager
+ (DataManager*)sharedInstance
{
#synchronized(self) {
if (_instance == nil) {
_instance = [[super allocWithZone:NULL] init];
// Allocate/initialize any member variables of the singleton class her
// example
//_instance.member = #"";
}
}
return _instance;
}
#pragma mark Singleton Methods
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance]retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
I hope it helps
I highly recommend using the SyntesizeSingleton header file first created by Matt Gallagher.
Find the latest version (that I know about) here:
https://github.com/cjhanson/Objective-C-Optimized-Singleton
It makes creating a singleton dead simple.
Here's an example header:
#import <Foundation/Foundation.h>
#interface Example : NSObject {}
+(Example*)sharedExample;
#end
And the corresponding .m:
#import "FMUser.h"
#import "SynthesizeSingleton.h"
#implementation Example
SYNTHESIZE_SINGLETON_FOR_CLASS(Example);
#end
[Example sharedExample] is created for you. It's pretty sweet.

NSTimer problem

So I am trying to set up a basic timer but I am failing miserably. Basically all I want is to start a 60 second timer when the user clicks a button, and to update a label with the time remaining(like a countdown). I created my label and button and connected them in IB. Next I created a IBAction for the button. Now when I tried to update the label based on the timer, my app screws up. Here's my code:
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector:#selector(updateLabelDisplay)
userInfo: nil repeats:YES];
I also have an updateLabelDisplay function that determines how many times the timer has ran and then subtracted that number from 60 and displays that number in the countdown label. Can anyone tell me what I am doing wrong?
Ok, well for starters, check this out if you haven't already: Official Apple Docs about Using Timers
Based on your description, you probably want code that looks something like this. I've made some assumptions regarding behavior, but you can suit to taste.
This example assumes that you want to hold on to a reference to the timer so that you could pause it or something. If this is not the case, you could modify the handleTimerTick method so that it takes an NSTimer* as an argument and use this for invalidating the timer once it has expired.
#interface MyController : UIViewController
{
UILabel * theLabel;
#private
NSTimer * countdownTimer;
NSUInteger remainingTicks;
}
#property (nonatomic, retain) IBOutlet UILabel * theLabel;
-(IBAction)doCountdown: (id)sender;
-(void)handleTimerTick;
-(void)updateLabel;
#end
#implementation MyController
#synthesize theLabel;
// { your own lifecycle code here.... }
-(IBAction)doCountdown: (id)sender
{
if (countdownTimer)
return;
remainingTicks = 60;
[self updateLabel];
countdownTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: #selector(handleTimerTick) userInfo: nil repeats: YES];
}
-(void)handleTimerTick
{
remainingTicks--;
[self updateLabel];
if (remainingTicks <= 0) {
[countdownTimer invalidate];
countdownTimer = nil;
}
}
-(void)updateLabel
{
theLabel.text = [[NSNumber numberWithUnsignedInt: remainingTicks] stringValue];
}
#end
It may be a little late to post a second answer to this question but I've been looking for a good place to post my own solution to this problem. In case it is of use to anyone here it is. It fires 8 times but of course this can be customised as you please. The timer deallocates itself when time is up.
I like this approach because it keeps the counter integrated with the timer.
To create an instance call something like:
SpecialKTimer *timer = [[SpecialKTimer alloc] initWithTimeInterval:0.1
andTarget:myObject
andSelector:#selector(methodInMyObjectForTimer)];
Anyway, here are the header and method files.
//Header
#import <Foundation/Foundation.h>
#interface SpecialKTimer : NSObject {
#private
NSTimer *timer;
id target;
SEL selector;
unsigned int counter;
}
- (id)initWithTimeInterval:(NSTimeInterval)seconds
andTarget:(id)t
andSelector:(SEL)s;
- (void)dealloc;
#end
//Implementation
#import "SpecialKTimer.h"
#interface SpecialKTimer()
- (void)resetCounter;
- (void)incrementCounter;
- (void)targetMethod;
#end
#implementation SpecialKTimer
- (id)initWithTimeInterval:(NSTimeInterval)seconds
andTarget:(id)t
andSelector:(SEL)s {
if ( self == [super init] ) {
[self resetCounter];
target = t;
selector = s;
timer = [NSTimer scheduledTimerWithTimeInterval:seconds
target:self
selector:#selector(targetMethod)
userInfo:nil
repeats:YES];
}
return self;
}
- (void)resetCounter {
counter = 0;
}
- (void)incrementCounter {
counter++;
}
- (void)targetMethod {
if ( counter < 8 ) {
IMP methodPointer = [target methodForSelector:selector];
methodPointer(target, selector);
[self incrementCounter];
}
else {
[timer invalidate];
[self release];
}
}
- (void)dealloc {
[super dealloc];
}
#end