Do I need to release an MKMapItem or placemark objects? - iphone

Of the tutorials I've found online (e.g. http://bit.ly/SIXlI5) and the Apple documentation, none make mention of releasing MKMapItem or the placemarks. Do they need to be released, and if not, why not? Does the Maps app take care of all that?
NSDictionary *address = #{
(NSString *)kABPersonAddressStreetKey: _address.text,
(NSString *)kABPersonAddressCityKey: _city.text,
(NSString *)kABPersonAddressStateKey: _state.text,
(NSString *)kABPersonAddressZIPKey: _zip.text
};
MKPlacemark *place = [[MKPlacemark alloc]
initWithCoordinate:_coords
addressDictionary:address];
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:place];
NSDictionary *options = #{
MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving
};
[mapItem openInMapsWithLaunchOptions:options];

If you're not using ARC, and you alloc an object, you are responsible for releasing it. That's all there is to it.
In this case, when you call openInMaps..., the map item info is encoded in a URL that's sent to the Maps app. But you dont need to worry about that.
You can expect that unless it's called out in headers or documentation, an instance method will do its work synchronously and not release the receiver.

Related

how to check if Mapkit is available or not?

I am building an application that is using Mapkit. I know that this is only available in IOS6.
So I should check if this is available or not. I am using the following code.
if(NSClassFromString(#"MKMapKit")) {
// MKMapKit is available in this OS
CLLocationCoordinate2D coords =
CLLocationCoordinate2DMake(51.097185,5.621653);
NSDictionary *address = #{
(NSString *)kABPersonAddressStreetKey: #"Weg naar oqdffds 59",
(NSString *)kABPersonAddressCityKey: #"Msfsf",
(NSString *)kABPersonAddressStateKey: #"Limbusqfqsdf",
(NSString *)kABPersonAddressZIPKey: #"3670",
(NSString *)kABPersonAddressCountryCodeKey: #"BE",
(NSString *)kABPersonPhoneMainLabel:#"04741234567"
};
MKPlacemark *place = [[MKPlacemark alloc]
initWithCoordinate:coords addressDictionary:address];
MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:place];
mapItem.phoneNumber = #"0141343252";
//current location
MKMapItem *mapItem2 = [MKMapItem mapItemForCurrentLocation];
NSArray *mapItems = #[mapItem, mapItem2];
NSDictionary *options = #{
MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving,
MKLaunchOptionsMapTypeKey:
[NSNumber numberWithInteger:MKMapTypeStandard],
MKLaunchOptionsShowsTrafficKey:#YES
};
[MKMapItem openMapsWithItems:mapItems launchOptions:options];
}else {
NSLog(#"tot hier");
// MKMapKit is not available in this OS
locationController = [[MyCLController alloc] init];
locationController.delegate = self;
[locationController.locationManager startUpdatingLocation];
}
But for some reason or another it always use the google method.
Can anybody help!
As already mentioned, MapKit has been available well before iOS 6.
What you want to check for is MKMapItem (not "MKMapKit").
However, as the documentation for MKMapItem explains (with a code example):
To determine whether a class is available at runtime in a given iOS
release, you typically check whether that class is nil. Unfortunately,
this test is not cleanly accurate for MKMapItem. Although this class
was publicly available starting with iOS 6.0, it was in development
prior to that. Although the class exists in earlier releases, you
should not attempt to use it in those releases.
To determine at runtime whether you can use map items in your
application, test whether the class and the
openMapsWithItems:launchOptions: class method exist. That method was
not added to the class until iOS 6.0. The code might look like the
following:
Class itemClass = [MKMapItem class];
if (itemClass && [itemClass
respondsToSelector:#selector(openMapsWithItems:launchOptions:)]) {
// Use class
}
So this check:
if(NSClassFromString(#"MKMapKit")) {
should be:
Class itemClass = [MKMapItem class];
if (itemClass && [itemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]) {
or:
Class itemClass = NSClassFromString(#"MKMapItem");
if (itemClass && [itemClass respondsToSelector:#selector(openMapsWithItems:launchOptions:)]) {
MkMapKit is available in ios 4.3, too, probably in 3.x, too!
What is new, is (like in all Releases), some new methods of MkMapKit:
You should better check for that specific method you need
(geocoding ?=
Look into the headers of MkMapKit you are importing (if i remeber correctly: MkMapKit.h), there are Macros defining the availability
of a specifc method, depending on the ios version.

MapKit in iOS 6 - How to Find Places Nearby...?

Using MapKit in iOS 6, how am I'm supposed to get nearby locations without having their specific coordinates? I'm also unsure if it's still possible...err...allowed...to use Google Maps API to accomplish this goal, as this is the only way I can think of to do this. I know everything is still in beta, but I've still found no information anywhere about this topic, on forums, in Apple's new MapKit Documentation, anywhere. All I want to do is perform a search for locations (let's say, parks, for example) within 'x' miles of the user's location.
It seems that since Apple has developed their own Maps application, they should have a way to accomplish this using MapKit or Core Location...right?
Try with this code. This may help to you.
URLManager *urlmanager = [[URLManager alloc] init];
urlmanager.delegate = self;
urlmanager.responseType = JSON_TYPE;
urlmanager.commandName = #"Search";
NSString *locationString = [NSString stringWithFormat:#"%f,%f",latitude,longitude];
//Location where you want to search
NSString *key = #"AIzaSyCNRpero6aM451X0IfgFHAd-Y3eJUssqoa8`enter code here`0E";
//This is the secret key which you will get from the Google API Console when you register your app.
NSString *radiuos = #"15000";
//This is the area within which you want to search
NSString *keyword = #"Hotel";//Search keyword you want to search
NSMutableDictionary *arguments = [[NSMutableDictionary alloc] init]; // zero argument
[arguments setValue:key forKey:#"key"];
[arguments setValue:locationString forKey:#"location"];
[arguments setValue:radiuos forKey:#"radius"];
[arguments setValue:#"true" forKey:#"sensor"];
[arguments setValue:keyword forKey:#"keyword"];
NSLog(#"Arguments are %#",arguments);
[urlmanager urlCallGetMethod:[NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/search/json"] withParameters:arguments];

Objective-C crash issue: NSInvalidArgumentException

I am stuck in it for a long time, but can not find a solution. Here is my code:`
NSLog(#"[tempArray retainCount]: %d",[tempArray retainCount]);
tempArray = [[NSMutableArray alloc] initWithArray:[allRemainingProductsDictionary objectForKey:[[allRemainingProductsDictionary allKeys]objectAtIndex:counter]]];
NSMutableDictionary *tempDictionary = [[NSMutableDictionary alloc] init];
[tempDictionary setObject:productName forKey:#"name"];
[tempArray release];
I am getting a NSException crash with this report. Please help.
The method getObjects:range: that is being sent to your NSDictionary instance is a NSArray method.
You're probably trying to to your initWithArray passing a NSDictionary instead of a NSArray.
Is the NSLog entry showing up? If not, it's because tempArray does not respond to retainCount. You don't need to worry about anything with release or retain if you're using Xcode 4.2 with ARC for iOS 5 (which you should, unless you have legacy code).
Otherwise, somewhere you're sending an object a message it doesn't respond to.

Memory management with NSDictionary

I found an answer to this a while ago, and made a mental note to fix it, but now I can't for the life of me find the post again.
Very simple - my current method for adding dictionaries to an array is leaky. Please, what is the best way to ensure they are being released properly? My method:
[beachPresenters addObject:[[NSMutableDictionary alloc]initWithObjectsAndKeys:
#"Kayak rides",#"name",#"kayak_sm.png",#"smPhoto",#"kayak_med",#"medPhoto",#"Free kayak rides for kids",#"description",#"",#"Friday",
#"All day! 10.00am - 6.00pm",#"Saturday",#"",#"Sun",#"Beach",#"stage",#"Blah blah blah",#"blurb",nil]];
beachPresenters (I assume it's an array) takes ownership of the dictionary, so the +1 to the reference count caused by +alloc/-init of the dictionary is not balanced. Thus, the dictionary is leaked.
Use the convenience method equivalent to balance the retain:
NSDictionary *presenter = [NSDictionary dictionaryWithObjectsAndKeys: ...];
[beachPresenters addObject: presenter];
Your array beachPresenters retains the mutable array you created, but your array as created in your example already has a retain count of one. So even if you dispose of beachPresenters, your dictionary will still be retained, i.e., leaked.
Use [NSMutableDictionary dictionaryWithObjectsAndKeys:] instead.
You can have implementation like below:
NSMutableDictionary *dict = [[NSMutableDictionary alloc]initWithObjectsAndKeys:
#"Kayak rides",#"name",#"kayak_sm.png",#"smPhoto",#"kayak_med",#"medPhoto",#"Free kayak rides for kids",#"description",#"",#"Friday",
#"All day! 10.00am - 6.00pm",#"Saturday",#"",#"Sun",#"Beach",#"stage",#"Blah blah blah",#"blurb",nil];
[beachPresenters addObject:dict];
[dict release];
or
NSMutableDictionary *dict = [[[NSMutableDictionary alloc]initWithObjectsAndKeys:
#"Kayak rides",#"name",#"kayak_sm.png",#"smPhoto",#"kayak_med",#"medPhoto",#"Free kayak rides for kids",#"description",#"",#"Friday",
#"All day! 10.00am - 6.00pm",#"Saturday",#"",#"Sun",#"Beach",#"stage",#"Blah blah blah",#"blurb",nil] autorelease];

Why am I having trouble with a deep copy in Objective C?

I'm assuming my understanding of how to perform a deep copy isn't just there yet. The same with some sub-optimal memory handling that I'm performing down below. This code below probably depicts a shallow copy, and I believe that's where my problem might be. I have some cookie-cutter code for an example that looks like the following:
NSArray *user = [[xmlParser createArrayWithDictionaries:dataAsXML
withXPath:kUserXPath] retain];
if([user count] > 0) {
self.name = [[user valueForKey:#"name"] copy];
}
// Crash happens if I leave the next line un-commented.
// But then we have a memory leak.
[user release];
[xmlParser release];
Unfortunately when I comment out [user release], the code works, but we have an obvious memory leak. The method createArrayWithDictionaries:withXPath: was refactored last night when the SO community helped me understand better memory management. Here's what it looks like:
- (NSArray *)createArrayWithDictionaries:(NSString *)xmlDocument
withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *dictionaries = [NSMutableArray array];
CXMLDocument *theXMLDocument = [CXMLDocument alloc];
theXMLDocument = [theXMLDocument initWithXMLString:xmlDocument
options:0
error:&theError];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
for (CXMLElement *xmlElement in nodes) {
NSArray *attributes = [xmlElement attributes];
NSMutableDictionary *attributeDictionary;
attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue]
forKey:[attribute name]];
}
[dictionaries addObject:attributeDictionary];
}
[theXMLDocument release];
return dictionaries;
}
I'm guessing there's a couple of issues that might be going on here:
Auto release on my dictionaries array is happening, thus my app crashing.
I'm not performing a deep copy, only a shallow copy. Thus when the user array is released, self.name is done for.
With NSZombieEnabled, I see the following:
*** -[CFString respondsToSelector:]:
message sent to deallocated instance 0x1ae9a0
Also, the final call where the backtrace shows this is crashing contains the following code in a separate module from the other two methods:
User *u = self.user;
NSString *uri = [NSString stringWithFormat:#"%#/user/%#/%#",
[self groupName], u.userId, kLocationsUri];
Between all the auto releasing/copies/retain happening between the client code and createArrayWithDictionaries:withXPath, I'm a bit confused as to the real problem here. Thanks again for helping me understand.
OK, you don't need to retain the return value from createArrayWithDictionaries: since you're not keeping it around. The return value is autoreleased. I'd strongly recommend reading up on how autoreleasing works. You only retain things that you intend to keep around in your object.
Also, user is an NSArray. If you call [user valueForKey:#"name"], you'll get another NSArray of values representing the values of the name key for each of the objects in users. Furthermore, how is the name property on your object defined? If you declared it as copy or retain (I believe retain is the default if you don't specify it yourself), you don't need to copy or retain the value. Indeed, the accessor should always be responsible for doing the memory management, not the caller. If you wrote your own accessor (i.e. you didn't use the #synthesize keyword), you need to make sure you do the memory management there.
I'm guessing what you meant to write was something more like this:
NSArray *user = [xmlParser createArrayWithDictionaries:dataAsXML withXPath:kUserXPath];
if ([user count] > 0)
self.name = [[user objectAtIndex:0] objectForKey:#"name"];
[xmlParser release];
I think your troubles are stemming from a misunderstanding of how memory management works in Objective-C.
Hope this helps.
Auto release on my dictionaries array is happening, thus my app crashing.
If the caller intends to keep the array around somewhere, it needs to retain it. Otherwise, it will crash when it tries to access the (now-deceased) object.
If the caller is going to store it in a property, it must use the self.dictionaries = […] syntax, not dictionaries = […]. The former is a property access, which calls the setter method; the latter is a direct instance variable assignment.
Coming back to your actual question, that of a deep copy: You need to get the sub-elements of every element and put them in each element's dictionary.
Basically, you need a recursive method (or a queue, but that's harder—file under premature optimization until you've proven you need it) that takes an element and returns a dictionary, and then you need to call this method on each of your element's child elements, and collect the results into an array and put that into the dictionary you're creating.
I would recommend making this recursive method an instance method of the element. Something like:
- (NSDictionary *) dictionaryRepresentation {
NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue] forKey:[attribute name]];
}
NSArray *childElements = [self childElements];
return [NSDictionary dictionaryWithObjectsAndKeys:
attributeDictionary, #"attributes",
[childElements valueForKey:#"dictionaryRepresentation"], #"childElements",
nil];
}
Then you replace the loop in createArrayWithDictionaries:withXPath: with a similar valueForKey: message. I'll leave you to fill it in.
valueForKey: is Key-Value Coding's principal method. In both places, we're making use of NSArray's handy implementation of it.
(If the use of valueForKey: still doesn't make sense to you, you should read the KVC Programming Guide. KVC is vitally important in modern Cocoa, so you do need to read this sooner or later.)