How to call setNeedsDisplayInRect using performSelectorOnMainThread? The problem is rect. I do not know how to pass the rect in performSelectorOnMainThread method. This method ask NSObject, but CGRect is not NSObject, it is just structure *.
//[self setNeedsDisplayInRect:rect];
[self performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:0 waitUntilDone:YES];
}
-(void)drawRect:(CGRect)rect {
/// drawing...
}
I need to invoke setNeedsDisplayInRect method in MainThread from not Main Thread.
Does anyone know how to do it?????????? Thanks in advance..
Really Thanks.
If you're on iOS 4.0 or later, you can use the following
dispatch_async(dispatch_get_main_queue(), ^{
[self setNeedsDisplayInRect:theRect];
});
On iOS 3.2 and earlier, you can set up an NSInvocation and run that on the main thread:
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:#selector(setNeedsDisplayInRect:)]];
[invocation setTarget:self];
[invocation setSelector:#selector(setNeedsDisplayInRect:)];
// assuming theRect is my rect
[invocation setArgument:&theRect atIndex:2];
[invocation retainArguments]; // retains the target while it's waiting on the main thread
[invocation performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:YES];
You may want to set waitUntilDone to NO unless you absolutely need to wait for this call to finish before proceeding.
Related
I am trying to write an block of code using OCMock's stub andDo method.
In this case UIImageView extension class is being tested. I want to check that the extension calls [self setImage:] with parameter that is non-nil (later other image comparison will be used).
When using OCMock's andDo method, the test crashes with EXC_BAD_ACCESS after the block completes.
id mockView = [OCMockObject mockForClass:[UIImageView class]];
[[[mockView stub] andDo:^(NSInvocation *invocation)
{
UIImage *img;
[invocation getArgument:&img atIndex:2]; <---- line causing the exception
somebodySetImage |= (img != nil);
}] setImage:OCMOCK_ANY];
[mockView do_something_that_calls_setImage];
The only solution that I've found for now is using andCall instead of andDo, but this complicates the test.
Can I avoid the crash with andDo?
UPDATE
Well, I will try to give a better example here:
Here is the new piece of the test code:
- (void)testDownloadingThumbnail
{
PInfo *_sut = [[PInfo alloc] init];
__block id target = nil;
id mock = [OCMockObject mockForClass:[NSOperationQueue class]];
[[[mock expect] andDo:^(NSInvocation *inv)
{
NSInvocationOperation *op;
[inv getArgument:&op atIndex:2];
target = [[op invocation] target]; /* replacing this line with STAssert does not help either */
}] addOperation:OCMOCK_ANY];
[_sut setDownloadQueue:mock];
[_sut startDownloadingImagesAsync:YES];
[mock verify];
STAssertEqualObjects(target, _sut, #"invalid op target");
}
Here is the tested code (single method from PInfo):
- (void)startDownloadingImagesAsync:(bool)isThumbnailImg
{
NSInvocationOperation *inv;
inv = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadThumbnailWorker:)
object:nil];
[[self downloadQueue] addOperation:inv];
}
The code still crashes upon exit from startDownloadingImagesAsync with EXC_BAD_ACCESS.
If I add a breakpoint inside the andDo block, I see that the control reaches this point and retrieves correct objects via getArgument.
Yet, if I use getArgument inside the block, it crashes whatever I try to do.
P.S. Thanks for help.
I ran into a similar problem when using NSProxy's forwardInvocation: method.
Can you try the below?
NSInvocationOperation *op; // Change this line
__unsafe_unretained NSInvocationOperation *op; // to this line
Or another approach could be to retain NSInvocation's arguments:
[invocation retainArguments];
http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html#//apple_ref/occ/instm/NSInvocation/retainArguments
I'll try to add a more detailed explanation later.
I think the problem is that you're trying to invoke a mock object directly. For what you're trying to do, you shouldn't need a mock object. Just call the method and verify that the image was set:
expect(myObject.imageView.image).to.beNil();
[myObject do_something_that_calls_setImage];
expect(myObject.imageView.image).not.to.beNil();
If you really want to use a mock for some reason, you could do it with a real UIImageView and a partial mock:
UIImageView *imageView = myObject.imageView;
id mockView = [OCMockObject partialMockForObject:imageView];
__block BOOL imageSet = NO;
[[[mockView stub] andDo:^(NSInvocation *invocation) {
UIImage *img;
[invocation getArgument:&img atIndex:2];
imageSet = (img != nil);
}] setImage:OCMOCK_ANY];
[myObject do_something_that_calls_setImage];
expect(imageSet).to.beTruthy();
In my case this was happening because I introduced another parameter to this method, so the block parameter got shifted by one.
I fixed it by changing [inv getArgument:&op atIndex:2] to [inv getArgument:&op atIndex:3]
This is my code:
[delegate performSelectorOnMainThread:#selector(setVariablePremierAffichage:) withObject:TRUE waitUntilDone:NO];
The problem is that the argument "withObject" only takes an "id" type, so, how can I cast my value "TRUE" to an id type? I also use ARC memory management in Xcode for iOS 5.
Pass an NSNumber. Use boolNumber = [NSNumber numberWithBool:TRUE]. Your method should be defined as:
-(void)setVariablePremierAffichage:(NSNumber *)boolNumber
{
BOOL value = [boolNumber boolValue];
// do something
}
Use CFbooleanreference and cast it
[delegate performSelectorOnMainThread:#selector(setVariablePremierAffichage:) withObject:(id)kCFBooleanTrue waitUntilDone:NO];
There is no way to cast a primitive to an id. If you need to call a method dynamically such as with performSelector you will need to use NSInvocation:
NSMethodSignature *sig = [self methodSignatureForSelector:#selector(setVariablePremierAffichage:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
BOOL yes = YES;
[invocation setArgument:&yes atIndex:2];
[invocation setTarget:self];
[invocation setSelector:#selector(setVariablePremierAffichage:)];
[invocation invoke];
Cheers!
I am an iOS newbie. I have a selector method as follows -
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second
{
}
I am trying to implement something like this -
[self performSelector:#selector(fooFirstInput:secondInput:) withObject:#"first" withObject:#"second" afterDelay:15.0];
But that gives me an error saying -
Instance method -performSelector:withObject:withObject:afterDelay: not found
Any ideas as to what I am missing?
Personally, I think that a closer solution to your needs is the use of NSInvocation.
Something like the following will do the work:
indexPath and dataSource are two instance variables defined in the same method.
SEL aSelector = NSSelectorFromString(#"dropDownSelectedRow:withDataSource:");
if([dropDownDelegate respondsToSelector:aSelector]) {
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[dropDownDelegate methodSignatureForSelector:aSelector]];
[inv setSelector:aSelector];
[inv setTarget:dropDownDelegate];
[inv setArgument:&(indexPath) atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
[inv setArgument:&(dataSource) atIndex:3]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
[inv invoke];
}
Because there is no such thing as a [NSObject performSelector:withObject:withObject:afterDelay:] method.
You need to encapsulate the data you want to send along into some single Objective C object (e.g. a NSArray, a NSDictionary, some custom Objective C type) and then pass it through the[NSObject performSelector:withObject:afterDelay:] method that is well known and loved.
For example:
NSArray * arrayOfThingsIWantToPassAlong =
[NSArray arrayWithObjects: #"first", #"second", nil];
[self performSelector:#selector(fooFirstInput:)
withObject:arrayOfThingsIWantToPassAlong
afterDelay:15.0];
You can package your parameters into one object and use a helper method to call your original method as Michael, and others now, have suggested.
Another option is dispatch_after, which will take a block and enqueue it at a certain time.
double delayInSeconds = 15.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self fooFirstInput:first secondInput:second];
});
Or, as you've already discovered, if you don't require the delay you can just use - performSelector:withObject:withObject:
The simplest option is to modify your method to take a single parameter containing both arguments, such as an NSArray or NSDictionary (or add a second method that takes a single parameter, unpacks it, and calls the first method, and then call the second method on a delay).
For instance, you could have something like:
- (void) fooOneInput:(NSDictionary*) params {
NSString* param1 = [params objectForKey:#"firstParam"];
NSString* param2 = [params objectForKey:#"secondParam"];
[self fooFirstInput:param1 secondInput:param2];
}
And then to call it, you can do:
[self performSelector:#selector(fooOneInput:)
withObject:[NSDictionary dictionaryWithObjectsAndKeys: #"first", #"firstParam", #"second", #"secondParam", nil]
afterDelay:15.0];
- (void) callFooWithArray: (NSArray *) inputArray
{
[self fooFirstInput: [inputArray objectAtIndex:0] secondInput: [inputArray objectAtIndex:1]];
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second
{
}
and call it with:
[self performSelector:#selector(callFooWithArray) withObject:[NSArray arrayWithObjects:#"first", #"second", nil] afterDelay:15.0];
You can find all the types of provided performSelector: methods here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html
There are a bunch of variations but there isn't a version that takes multiple objects as well as a delay. You'll need to wrap up your arguments in an NSArray or NSDictionary instead.
- performSelector:
- performSelector:withObject:
- performSelector:withObject:withObject:
– performSelector:withObject:afterDelay:
– performSelector:withObject:afterDelay:inModes:
– performSelectorOnMainThread:withObject:waitUntilDone:
– performSelectorOnMainThread:withObject:waitUntilDone:modes:
– performSelector:onThread:withObject:waitUntilDone:
– performSelector:onThread:withObject:waitUntilDone:modes:
– performSelectorInBackground:withObject:
I dislike the NSInvocation way, too complex. Let’s keep it simple and clean:
// Assume we have these variables
id target, SEL aSelector, id parameter1, id parameter2;
// Get the method IMP, method is a function pointer here.
id (*method)(id, SEL, id, id) = (void *)[target methodForSelector:aSelector];
// IMP is just a C function, so we can call it directly.
id returnValue = method(target, aSelector, parameter1, parameter2);
I just did some swizzling and needed to call the original method. What I did was making a protocol and cast my object to it.
Another way is to define the method in a category, but would need suppression of a warning (#pragma clang diagnostic ignored "-Wincomplete-implementation").
A simple and reusable way is to extend NSObject and implement
- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments;
something like:
- (void)performSelector:(SEL)aSelector withObjects:(NSArray *)arguments
{
NSMethodSignature *signature = [self methodSignatureForSelector: aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: signature];
[invocation setSelector: aSelector];
int index = 2; //0 and 1 reserved
for (NSObject *argument in arguments) {
[invocation setArgument: &argument atIndex: index];
index ++;
}
[invocation invokeWithTarget: self];
}
I would just create a custom object holding all my parameters as properties, and then use that single object as the parameter
I'm calling a selector on background thread,
The selector has NSAutorelasePool around it.
I guess the arguments I pass to the selector is causing the problem.
How should I deal with it?
SEL theSelector;
NSMethodSignature *aSignature;
NSInvocation *anInvocation;
theSelector = #selector(changeColor:forColorString:);
aSignature = [[animationData class] instanceMethodSignatureForSelector:theSelector];
anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:theSelector];
[anInvocation setTarget:animationData];
// indexes for arguments start at 2, 0 = self, 1 = _cmd
[anInvocation setArgument:¤tColor atIndex:2];
[anInvocation setArgument:&nsColorString atIndex:3];
[anInvocation performSelectorInBackground:#selector(invoke) withObject:NULL];
When you tell the invocation to perform invoke in the background, the new thread is created with invoke being the first method called. Invoke does not create an autorelease pool, so anything autoreleased during that method will be leaked.
To fix this, use a wrapper method to perform the invocation.
- (void)performInvocation:(NSInvocation *)anInvocation {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
[anInvocation invoke];
[pool release];
}
//where you were performing the invoke before:
[self performSelectorInBackground:#selector(performInvocation:) withObject:anInvocation];
In addition to what ughoavgfhw said, you also need to call [anInvocation retainArguments] if you intend to set objects as arguments and pass to a background thread.
I've come across an issue with using a third party library, and am not sure what the common pattern to solve it is.
I'm using the asi-http-request class, which fetches http objects asynchronously using a thread.
In my objects dealloc() method, I do
[request setDelegate:nil];
[request release];
However the delegate is sometimes still called after this has happened. (I can see when this happens the delegate field of the request object is nil.) This sometimes causes a crash if the delegate has been destroyed already.
I believe this is a race condition. The code from ASIHTTPRequest that calls the delegate looks like this:
// Let the delegate know we are done
if ([self didFinishSelector] && [[self delegate] respondsToSelector:[self didFinishSelector]]) {
[[self delegate] performSelectorOnMainThread:[self didFinishSelector] withObject:self waitUntilDone:[NSThread isMainThread]];
}
The problem happens if the performerSelectorOnMainThread has been called (but not completed) when the setDelegate call happens on the main thread.
One solution would be to add a wrapper around 'didFinishSelector' that checks (on the main thread) that the delegate is still non-nil before calling the selector, but this would result in a lot of wrappers.
There is some background here:
http://groups.google.com/group/asihttprequest/browse_thread/thread/721220b9645f4a42
All suggestions on the "normal" solution for this appreciated!
Thanks
Joseph
My original thoughts (wrapper around 'didFinishSelector' that checks on the main thread that the delegate is still non-nil before calling the selector) turned out to be the correct solution, as confirmed by helpful folks over on the apple dev forums:
https://devforums.apple.com/message/255935#255935
To avoid my worry of ending up with lots of wrappers, I managed to create only a single wrapper:
- (void)callSelectorCallback:(SEL *)selectorPtr withTarget:(id *)targetPtr
{
id target = *targetPtr;
SEL selector = *selectorPtr;
if (!selector || !target)
return;
if ([target respondsToSelector:selector])
{
[target performSelector:selector withObject:self];
}
}
- (void)callSelector:(SEL *)selector withDelegate:(id *)target
{
if (!*selector || !*target)
return;
SEL callback = #selector(callSelectorCallback:withTarget:);
NSMethodSignature *signature = [ASIHTTPRequest instanceMethodSignatureForSelector:callback];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:callback];
[invocation setTarget:self];
[invocation setArgument:&selector atIndex:2];
[invocation setArgument:&target atIndex:3];
[invocation performSelectorOnMainThread:#selector(invoke) withObject:nil waitUntilDone:[NSThread isMainThread]];
}
then when I want to call the delegate, the code just looks something like this:
[self callSelector:&didFinishSelector withDelegate:&delegate];
As best I can tell from experiments & code analysis (and assuming setDelegate is only called from the main thread), this is 100% safe. It could be made safe for the non-main thread calls to setDelegate by taking the object lock inside callSelectorCallback.
For dealing with objects across threads, you should almost always retain them. Basically,
id delegate = [[self delegate] retain];
if ([self didFinishSelector] && [delegate respondsToSelector:[self didFinishSelector]]) {
[delegate performSelectorOnMainThread:[self didFinishSelector]
withObject:self
waitUntilDone:[NSThread isMainThread]];
}
[delegate release];
Technically, the delegate could be dealloc-ed in between [self delegate] and the subsequent retain, and I'm not at all sure if Apple's #synthesized atomic accessors protect against this or not, but I believe the only way to solve this is in the accessor,
[[delegate retain] autorelease];
best of luck, race conditions get the best of us!