How can I pass multiple objects to a delayed method? - ios

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.

Related

Executing loop values one after another in xcode

By adding this code in viewDidLoad :
NSMutableArray *array = [NSMutableArray arrayWithObjects:
#"Hefeweizen", #"IPA", #"Pilsner", #"Stout", nil];
NSLog(#"array %#",array);
This is the NSLog I get :
array (
Hefeweizen,
IPA,
Pilsner,
Stout
)
How to get values one by one instead of showing total value in NSLog for example:
array{hefeweizen}
array{IPA}
array{Pilsner}
array{Stout}
Ex 1: array[0],array[1]....;
Ex 2: [array objectAtIndex:0]..;
//Initialise More data
setMoreArray = [[NSMutableArray alloc] init];
NSMutableDictionary *setData = [[NSMutableDictionary alloc] init];[setData setObject:#"profile.png" forKey:#"icon"];[setData setObject:#"inloyal Profile" forKey:#"lable"];[setMoreArray addObject:setData];
some thing like this ??
op:
array ->
(
{
"icon" = "profile.png"
},
{
"lable" = "inloyal Profile"
}
)
I tried this code and it worked correctly :
NSUInteger index;
int64_t delayInSeconds = 5.0;
NSMutableArray *array = [NSMutableArray arrayWithObjects:
#"Hefeweizen", #"IPA", #"Pilsner", #"Stout", nil];
NSMutableArray *arrayac = [[NSMutableArray alloc]init];
index = 0;
for ( id item in array )
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[arrayac insertObject:item atIndex:index];
NSLog(#"dest array %# ",arrayac);
NSLog(#"last object in the array %# ",[arrayac lastObject]);
testLabel.text = [arrayac lastObject];
});
delayInSeconds += 1;
index++;
}
I placed this code in viewDidLoad and after running the code, exactly after 5sec delay the label changed one after another in a interval of 1sec. I wanted is to show the all values in the array showing up one by one.

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.

EXC_BAD_ACCESS on performSelectorInBackground

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).

how to alter NSMutableArray in an specific array

I have a NSMutableArray wich holds a Custom class of MKMapkit.
MapPoint *placeObject = [[MapPoint alloc] initWithTitle:title subtitle:subtitle coordinate:loc description:description storeImage:storeImage];
[annotationArray addObject:placeObject];
My routine fills the placeObject without the image because they will load asynchronously and finished after a couple of seconds. Now my questions is: Is there a way to alter the placeObject which is within the annotationArray?
EDIT
To display my issue in a better way here some code parts:
if (annotationArray == nil)
{
[self setAnnotationArray:[[NSMutableArray alloc] init]];
}
for (NSDictionary *locationDetails in parser.items)
{
__block NSData *storeImageData;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
storeImageData = [NSData dataWithContentsOfURL:storeImageURL];
dispatch_sync(dispatch_get_main_queue(), ^{
UIImage *storeImageTMP = [UIImage imageWithData:storeImageData];
counterBlock ++;
NSLog(#"async: %d", counterBlock);
[imageArray addObject:storeImageTMP];
});
});
MapPoint *placeObject = [[MapPoint alloc] initWithTitle:title subtitle:subtitle coordinate:loc description:description storeImage:storeImage];
[annotationArray addObject:placeObject];
}
[mapView addAnnotations:annotationArray];
after this is done: dispatch_queue_t will start working. So I check every 2 seconds if its fully loaded. Then i start to alter annotationArray and refresh my annotations (remove all -> add to map witch additional image)
- (void) checkMap
{
if (counterBlock == 547)
{
for (int i=0; i<=counterBlock; i++)
{
MapPoint *point = annotationArray[i];
point.storeImage = imageArray[i];
}
if(timer)
{
[timer invalidate];
timer = nil;
}
[mapView removeAnnotations:mapView.annotations];
[mapView addAnnotations:annotationArray];
}
}
All you need to do is get a reference to one of the MapPoint objects in the array. Then you can set properties and/or call methods on the object as needed.
MapPoint *point = annotationArray[x]; // x is whatever index you need
point.image = ... // some image
You can retrieve from your mutable array, the object you want to alter with the objectAtIndex method
NSInteger indexofMyObject = 0;
MapPoint *point = [myArray objectAtIndex:indexofMyObject];
Your MapPoint instance placeObject is a pointer. So adding it to the array does not copy placeObject by value, but actually holds its address. Getting the address of the object from the array will allow you to manipulate it.
annotationArray[objectIndex].anything = anything you want

multithreading in iOS, MapKit displaying data

My app is MapKit based, where multiple users can be tracked. Now using our web services, I am displaying my location on the map plus other users' last, let's say 10 locations. If a user updates their location, it is sent through the web service and displayed on the maps via call back. I am able to track other users in real time but don't know how to use Threading here. My UI is blocking at times and also crashing sometimes due to memory issue.
In my connectionDidFinishLoading method, I am parsing JSON data and then creating annotations and overlay:
-(void) connectionDidFinishLoading: (NSURLConnection *) connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *trackingDict = [NSJSONSerialization JSONObjectWithData:empJsonData options:kNilOptions error:nil];
NSLog(#"Json Dictionary = %#", trackingDict);
NSLog(#"COUNT = %i",trackingDict.count);
if ([trackingDict count] >= 2) {
for (trackUsersCount = 0; trackUsersCount< trackingDict.count; trackUsersCount++) {
NSLog(#"trackUsersCount %i", trackUsersCount);
NSMutableArray *latlongArray = [[NSMutableArray alloc]init];
latlongArray = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"latlong"];
[userLongitudeArray removeAllObjects];
[userLatitudeArray removeAllObjects];
for (int i = 0; i<latlongArray.count; i++) {
NSLog(#"COunt - > %#", [[latlongArray objectAtIndex:i]objectForKey:#"lat"]);
NSLog(#"COunt - > %#", [[latlongArray objectAtIndex:i]objectForKey:#"long"]);
[userLatitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"lat"]];
[userLongitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"long"]];
}
// ProfilePIC URL
profilePicURLString = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_profilePicture"];
NSString *name = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_firstName"];
[userNameArray addObject:name];
[profilePicURLStringArray addObject:profilePicURLString];
for (int i = 0; i<userLatitudeArray.count; i++) {
CLLocationCoordinate2D userLocation;
userLocation.latitude = [[userLatitudeArray objectAtIndex:i]doubleValue];
userLocation.longitude = [[userLongitudeArray objectAtIndex:i] doubleValue];
Annotation *Anno = [[Annotation alloc]init];
Anno.coordinate = userLocation;
Anno.title = name;
Anno.userProfileImageString = profilePicURLString;
[mapView addAnnotation:Anno];
}
NSLog(#"ARRAY for longitude %#", userLongitudeArray);
NSLog(#"ARRAY for latitude %#", userLatitudeArray);
int i;
for (i = 0; i<userLatitudeArray.count; i++) {
CLLocationCoordinate2D userLocation;
userLocation.latitude = [[userLatitudeArray objectAtIndex:i]doubleValue];
userLocation.longitude = [[userLongitudeArray objectAtIndex:i] doubleValue];
MKMapPoint * pointsArray = malloc(sizeof(CLLocationCoordinate2D)*userLongitudeArray.count);
pointsArray[i] = MKMapPointForCoordinate(userLocation);
polyline = [MKPolyline polylineWithPoints:pointsArray count:i];
free(pointsArray);
}
[mapView addOverlay:polyline];
}
}
[mapView reloadInputViews];
}
}
The web service is called after every 20 seconds, I know I can user GCD here or other threading approach but at the time-interval when web service is called via background thread, the annotations and overlays are not displayed not the map.
Any help is much appreciated!
I don't think there's an quick & easy fix for this. One approach would be to separate the code that collects (modifies) the data from the code that updates the view.
Set the code that is dealing with UI into a separate method, updateUI for example.
From here on you have couple of choices. You could try this for example:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//perform the data collection, calculations...
//here is where the model in MVC gets modified
//
[self performSelectorOnMainThread: #selector(updateUI) withObject:nil waitUntilDone: NO];
}
-(void)updateUI
{
//do the UI updates (like adding overlays etc...) here
}
You could also store all the data needed to update the UI to a kind of object and pass it as withObject: parameter.

Resources