EXC_BAD_ACCESS on performSelectorInBackground - ios

I want to call this method in background,
-(void)downloadImage_3:(NSString* )Path AtIndex:(int)i
I am calling in this way but it crashes and shows EXC_BAD_ACCESS
[self performSelectorInBackground:#selector(downloadImage_3:AtIndex:) withObject:[NSArray arrayWithObjects:#"http://www.google.com",i, nil]];
How to call downloadImage_3: method in background ?
where i am doing mistake ?

try this
[self performSelectorInBackground:#selector(downloadImage_3:AtIndex:) withObject:[NSArray arrayWithObjects:#"http://www.google.com",i, nil] afterDelay:15.0];
Or try this
NSString* number = [NSString stringWithFormat:#"%d",i];
NSArray* arrayValues = [[NSArray alloc] initWithObjects:[[msg_array objectAtIndex:i] valueForKey:#"Merchant_SmallImage"],number, nil];
NSArray* arrayKeys = [[NSArray alloc] initWithObjects:#"Path",#"Index",nil];
NSDictionary* dic = [[NSDictionary alloc] initWithObjects:arrayValues forKeys:arrayKeys];
[self performSelectorInBackground:#selector(downloadImage_3:) withObject:dic];
Define downloadImage_3 function like this:
-(void)downloadImage_3:(NSDictionary *)dic
{
NSString *path = [dic valueForKey:#"Path"];
int i = [[dic valueForKey:#"Index"] intValue];
//Your code
}

You can not call parameterized function in #selector. Create NSDictionary and then pass that dictionary in withObject:
int atIndex = 2; // whatever index you want to pass
NSArray *arr = [NSArray arrayWithObjects:obj1,obj2,nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:atIndex],#"AtIndex",arr,#"ImagesUrl", nil];
[self performSelectorInBackground:#selector(downloadImage_3:) withObject:dictionary];
Define downloadImage_3 function like this:
-(void)downloadImage_3:(NSDictionary *)dic {
// use dic and write your code
}

You can't use performSelectorInBackground:withObject: for selectors that take multiple arguments. The suggestions other answers give sort of work, but they all assume you can manipulate the method you are calling, which is not always possible (or even a good idea for clarity).
I recommend using NSInvocation instead, as it allows multiple arguments, or alternatively using GCD to dispatch a block to a background queue (or any queue other than the main for that matter).
Here's an example usage of NSInvocation
NSMethodSignature *sig = [[self class] instanceMethodSignatureForSelector:#selector(downloadImage_3:AtIndex:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
[inv setTarget:self];
[inv setSelector:#selector(downloadImage_3:AtIndex:)];
[inv setArgument:(__bridge void *)#"http://www.google.com" atIndex:2];
NSUInteger i = 1;
[inv setArgument:&i atIndex:3];
[inv performSelectorInBackground:#selector(invoke) withObject:nil];
It's worth double checking, I wrote the code in the browser so I might've missed something the compiler will pick up.
As additional note, you should really re-think your method naming conventions, a much more standard way to name methods would have the method be named -(void)downloadImage3:(NSString* )path atIndex:(int)i. Notice how atIndex begins with lowercase and how there is no underscore (which just looks weird in the middle of a method name). Also it might be worth noting that using NSUInteger is preferred for the index, as NSArray in general works with NSUIntegers (both should work, but there are cases in which int might not suffice).

Related

See the variable in the same method=null ios

I use library soap the return of this function in on nsdictionary i change it into nsarray after i close ];} and type nslog it give that nsarray =null but if i put it before close of the method in the method give data
- (void)showlinks
{
SYSoapClient *show_links_obj = [SYSoapClient new];
links_tags = [[NSMutableArray alloc] initWithObjects:#"empid", #"type", nil];
links_vars = [[NSMutableArray alloc] initWithObjects:txt_username, type_user, nil];
[show_links_obj callSoapServiceWithParameters__functionName:#"getlinks"
tags:links_tags
vars:links_vars
callback:^(NSDictionary *result,
BOOL response)
{
link_raw_Data = [result allValues];
link_con_Data = [link_raw_Data componentsJoinedByString:#""];
//NSArray *links = [con_Data componentsSeparatedByString:#"#"];
links = [link_con_Data componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"#^"]];
NSUInteger x = links.count;
NSLog(#"%#",link_raw_Data);
//NSLog(#"%d",x);
//NSLog(#"%#",links[5]);
}];
NSLog(#"%#",links[5]);
}
Judging by the callback argument to the soap service, it's an asynchronous call. This means that it will return right away, do the networking on a background thread and call the callback sometime later when it is done.
Since it's asynchronous and doesn't wait/block, the log statement just after it will be executed before getting any data back from the soap service.

NSInvocation returns value but makes app crash with EXC_BAD_ACCESS

I have an array which I am iterating and looking for a particular flag. If the flag value is nil, I am calling a method which generates an invocation object and returns the result of invocation.
My code structure is as follows
for(NSString *key in [taxiPlanes allKeys])
{
Plane *currentPlane = [taxiPlanes objectForKey:key];
if(currentPlane.currentAction == nil)
{
NSString *selector = [[currentPlane planeTakeoffSequence] firstObject];
currentPlane.currentAction = selector;
// Calling for NSInvocation in [self ...]
NSArray *action = [NSArray arrayWithArray:[self operationFromTakeoffAction:currentPlane.currentAction AtPoint:currentPlane.position]];
NSLog(#"%#",action);
}
}
Method which generates NSInvocation
-(NSArray *) operationFromTakeoffAction:(NSString *) action AtPoint:(CGPoint) flightPoint
{
NSMethodSignature *methodSignature = [FlightOperations instanceMethodSignatureForSelector:NSSelectorFromString(action)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setTarget:fOps];
[invocation setSelector:NSSelectorFromString(action)];
[invocation setArgument:&flightPoint atIndex:2];
NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];
return resultSet;
}
In the for loop, without the method call for NSInvocation ([self ....]), the loop just executes fine and not crashing. But when I introduce the method to invoke NSInvocation, I am able to see the NSLog in for loop prints expected NSArray result but it crashes with error message EXC_BAD_ACCESS.
I am not able to figure out why it fails even though NSInvocation returns proper result. Without NSInvocation, for loop is not getting crashed.
Any suggestions would be helpful.
Thanks
I am guessing you are using ARC?
The problem is with the line [invocation getReturnValue:&resultSet];. getReturnValue: just copies the bytes of the return value into the given memory buffer, regardless of type. It doesn't know or care about memory management if the return type is a retainable object pointer type. Since resultSet is a __strong variable of object pointer type, ARC assumes that any value that has been put into the variable has been retained, and thus will release it when it goes out of scope. That is not true in this case, so it crashes. (Also, the array that you had resultSet originally point to will be leaked, since getReturnValue: overwrites that value without releasing it. Why you even made that variable point to an object in the first place is beyond me.)
The solution is that you must give a pointer to a non-retained type to getReturnValue:. Either:
NSArray * __unsafe_unretained tempResultSet;
[invocation getReturnValue:&tempResultSet];
NSArray *resultSet = tempResultSet;
or:
void *tempResultSet;
[invocation getReturnValue:&tempResultSet];
NSArray *resultSet = (__bridge NSArray *)tempResultSet;
Yes, that's just happenedIn the ARC。
I guess this is the system Bug.
For example:
【iPhone4s + iOS8.4】、【 iphone 4 + iOS7.1】 (crash),
【iPhone6 + iOS9.3】、【 iphone 5 + iOS8.4.1】 (pass),
my test demo download link https://github.com/leopardpan/IssuesDemo
The original code
NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];
To solve the following
case 1:
void *temp = NULL;
[invocation invoke];
[invocation getReturnValue:&temp];
NSArray *resultSet = (__bridge NSArray*)temp;
case 2:
__weak NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];
case 3:
__autoreleasing NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];
case 4:
__unsafe_unretained NSArray *resultSet = [NSArray alloc]init];
[invocation invoke];
[invocation getReturnValue:&resultSet];
Recommended to use case1, principle should be #newacct said
Welcome to discuss
This is the solution for cases where you don't know what type is the return value
__weak id weakReturnValue;
[_invocation getReturnValue:&weakReturnValue];
id returnValue = weakReturnValue; // ARC owned strong reference

Create NSMutableArray dynamically, and use selector with parameters on objects

I'm creating an application, in which i used an NSMutableArray to stock some objects. To do this, no problem in first look :
ArrayOfViews = [[NSMutableArray alloc] init];
[ArrayOfViews addObject:...];
But my object are UIViews, that i create. For example, I have a file named "Level1". How could I add an object from "Level1", like :
Level1 * level1view;
decalered in the same UIViewcontroller from my NSMuttableArray ?
Like a sort of :
for (i = 0, i < max, i++)
{
[ArrayOfViews addObject:[objectWithName:[NSString stringWithFormat:#"level%iview", i]]];
}
I don't know how could I wrote it with good encoding.
Second, to use the objects with a selector and parameters, how could i do ?
Because i tried :
NSString * futureSelector = [NSString stringWithFormat:#"Level%iappearswithTime:", number];
SEL s = NSSelectorFromString(futureSelector);
NSInvocation * invoc = [[NSInvocation alloc] init];
[invoc setSelector:s];
[invoc setArgument:&t atIndex:1];
[invoc setTarget:[ArrayOfViews objectAtIndex:number]];
[invoc invoke];
To replace this sort of code :
[level1view Level1appearswithTime:t];
where variable t is an NSTimeInterval
Thanks for your help !
Since variable names are gone during compilation, you essentially can't do this (unless, of course, the variables are instance variables in which case their name is preserved, but then you still don't want to do it, I'm sure.)
As to the selector-and-string-problem: why not use NSStringFromSelector(), NSSelectorFromString() and - [NSObject performSelector:withObject:]?
SEL s = NSSelectorFromString([selectorsArray objectAtIndex:0]);
[someObject performSelector:s withObject:42];

How can I pass multiple objects to a delayed method?

How can I pass
-(void)explosionFromPoint:(CGPoint)explosionPoint withSprite:(CCSprite*)sprite;
in a
[self performSelector:#selector(//Right here) withObject:nil afterDelay:3];?
You cant put the whole selector inside the #selector(), and withObjectfirst of all only allows one object to be passed over, and neither do I understand how to use it.
How can I pass a method with objects after a delay?
I also tried a workaround where I call
[self performSelector:#selector(waitExplosion) withObject:nil afterDelay:3];
which then runs the action itself, [self explosionFromPoint:c0TileCoord withSprite:bomb];
, but this is a really bad way to do it as I have to re-declare the variables and it's just bad.
How can I pass a method with objects after a delay?
You could use dispatch_after.
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self explosionFromPoint:aPoint withSprite:sprite];
});
Where aPoint and sprite are defined outside of your block.
If you want to pass multiple arguments to a method using performSelector: , keep all the arguments in a NSArray or NSDictionary and then pass that array/dictionary like
[self performSelector:#selector(testWith:) withObject:array afterDelay:3];
Edit
NSArray *array =[NSArray arrayWithObjects:#"arg1",#"arg2"];
[self performSelector:#selector(testWith:) withObject:array afterDelay:3];
-(void)testWith:(NSArray *)array
{
NSString * arg1 =[array objectAtIndex:0];// first argument
NSString *arg2 = [array objectAtIndex:1];// second argument
// do other stuff
}
Another aproach you can use is to pass an NSArray or an NSMutableArray as parameter to your method.
An Example:
NSString *firstItem = #"first Item";
NSNumber *secondItem = [[NSNumber numberWithBool:YES];
YourCustomObject *thirdItem= [[YourCustomObject alloc] init];
//your array to pass
NSArray *arrayToPass = [[NSArray arrayWithObjects:firstItem, secondItem, thirdItem, nil];
//call you method after delay and pass all the objects:
[self oerformSelector:#selector(doStuffWithMultipleParams:) withObject:arrayToPass afterDelay:3.0f];
The method could be used like this:
- (void)doStuffWithMultipleParams:(NSArray *)passedArray{
String *s;
BOOL b;
YourCustomObject *obj;
if ([[passedArray objectAtIndex:0] isKindOfClass:[NSString class]]){
s = [passedArray objectAtIndex:0];
}
if ([[passedArray objectAtIndex:1] isKindOfClass:[NSNumber class]]){
b = [[passedArray objectAtIndex:1] boolValue];
}
if ([[passedArray objectAtIndex:1] isKindOfClass:[YourCustomObject class]]){
obj = [passedArray objectAtIndex:2];
}
if (s || b || obj){
//now do stuff with these objects
}
}
As per the documentation. For your case you can make use of another method of NSObject. ie
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
But unfortunately in this method you can not give the delay.

memory management for CTRunDelegateRef iPhone

I focus on a project to add image support based on OmniGroup's rtf editor.I met the problem when I use CTFramesetterCreateWithAttributedString, and it receives
EXC_BAD_ACCESS
which is caused by a zombie object.
For simplicity, I get the NSAttributedString from a rtfd file which only has a photo. I add the method to parser the image tag on iOS based on the Omni's sample. And the part to create the NSAttributedString is followed by this tutorial from raywenderlich, and looks like:
CTRunDelegateCallbacks callbacks;
callbacks.version = kCTRunDelegateCurrentVersion;
callbacks.getAscent = ascentCallback;
callbacks.getDescent = descentCallback;
callbacks.getWidth = widthCallback;
callbacks.dealloc = deallocCallback;
CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, imgAttr);
NSDictionary *imgAttr = [NSDictionary dictionaryWithObjectsAndKeys:imgWidth,kImageWidth,imgHeight,kImageHeight, nil];//recored the width and height
NSDictionary *attrDictionaryDelegate = [NSDictionary dictionaryWithObjectsAndKeys:(id)delegate, (NSString*)kCTRunDelegateAttributeName,nil];
[_attributedString appendString:#" " attributes:attrDictionaryDelegate];
CFRelease(delegate);
here appendString:attributes: is provided by Omni and is a category for NSMutableAttributedString:
- (void)appendString:(NSString *)string attributes:(NSDictionary *)attributes;
{
NSAttributedString *append;
append = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[self appendAttributedString:append];
[append release];
}
In the Parser class I test CTFramesetterCreateWithAttributedString and it creates successfully and no memory problem happens. However, when I call CTFramesetterCreateWithAttributedString in my view class, it receives the EXC_BAD_ACESS problem. I send the CTFramesetterCreateWithAttributedString to my view in the viewDidLoad:
NSArray *arr = [OUIRTFReader parseRTFString2:rtfString];
self.editFrame.attributedText = [arr objectAtIndex:0];
//for OUIRTFReader
+ (NSArray *)parseRTFString2:(NSString *)rtfString
{
OUIRTFReader *parser = [[self alloc] _initWithRTFString:rtfString];
NSMutableArray *temp = [NSMutableArray arrayWithCapacity:1];
NSAttributedString *tempAstr = [[NSAttributedString alloc] initWithAttributedString:parser.attributedString];
[temp addObject:tempAstr];
[tempAstr release];
if (parser.zjImageArray) {
[temp addObject:parser.zjImageArray];
}
[parser release];
}
here editFrame is ivar of OUIEditableFrame provided by OmniFramework.
After that, in the layoutSubview: of OUIEditableFrame, CTFramesetterCreateWithAttributedString failed. Profiling with instrument, it points there is a zombie object in :
NSDictionary *imgAttr = [NSDictionary dictionaryWithObjectsAndKeys:imgWidth,kImageWidth,imgHeight,kImageHeight, nil];
It demonstrates that
An Objective-C message was sent to a deallocated object(zombie) at address
I'm not so familiar with core foundation like as UIKit or others. So I'd like to know what's wrong with the method to create a attributed string for image with CTRunDelegateRef in my code?
Thanks!
CTRunDelegateCreate create a delegate with an arbitrary context pointer (void *). That means the dictionary imgAttr isn't retained by the delegate.
You need to retain the context dictionary when creating the delegate and release it in the dealloc callback.

Resources