MapKit based app crashing when loading from plist - arrays

I'm writing a program which uses MapKit to display a map which will load custom annotations from a plist file. Each annotation is a dictionary item in the root array, with a title, subtitle, latitude, and longitude. When I hard-coded in the annotations for test purposes, the program worked beautifully. But with the addition of the MapDemoAnnotation class and my attempt to read in the property list, the program crashes upon launch.
Here is my annotation implementation:
#import "MapDemoAnnotation.h"
#implementation MapDemoAnnotation
#synthesize coordinate;
#synthesize title;
#synthesize subtitle;
-(id)initWithDictionary:(NSDictionary *)dict{
self = [super init];
if(self!=nil){
coordinate.latitude = [[dict objectForKey:#"latitude"] doubleValue];
coordinate.longitude = [[dict objectForKey:#"longitude"] doubleValue];
self.title = [dict objectForKey:#"name"];
self.subtitle = [dict objectForKey:#"desc"];
}
return self;
}
-(void)dealloc{
[title release];
[subtitle release];
[super dealloc];
}
#end
I'm guessing the viewDidLoad method in my RootViewController class is the problem, though.
- (void)viewDidLoad {
[super viewDidLoad];
MKMapView *mapView = (MKMapView*)self.view;
mapView.delegate = self;
mapView.mapType=MKMapTypeHybrid;
CLLocationCoordinate2D coordinate;
coordinate.latitude = 39.980283;
coordinate.longitude = -75.157568;
mapView.region = MKCoordinateRegionMakeWithDistance(coordinate, 2000, 2000);
//All the previous code worked fine, until I added the following...
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Locations" ofType:#"plist"];
NSData* data = [NSData dataWithContentsOfFile:plistPath];
NSMutableArray* array = [NSPropertyListSerialization propertyListFromData:data
mutabilityOption:NSPropertyListImmutable
format:NSPropertyListXMLFormat_v1_0
errorDescription:nil];
if (array) {
NSMutableDictionary* myDict = [NSMutableDictionary dictionaryWithCapacity:[array count]];
for (NSDictionary* dict in array) {
MapDemoAnnotation* annotation = [[MapDemoAnnotation alloc]initWithDictionary:dict];
[mapView addAnnotation:annotation];
[annotation release];
}
NSLog(#"The count: %i", [myDict count]);
}
else {
NSLog(#"Plist does not exist");
}}
The program crashes for reasons I cannot figure, but I figure I must have done something wrong in reading in the property list or else in the MapDemoAnnotation class. Am I missing something obvious, or making a novice mistake?
My code is largely borrowed, so I could be way off base with how I'm approaching it.
Thanks in advance!

The third parameter in the call to propertyListFromData is wrong. The compiler must be giving you a "makes pointer from integer without a cast" warning there because the format parameter expects a pointer to a NSPropertyListFormat variable (so the method can return the format to you). So you need to do:
NSPropertyListFormat propertyListFormat;
NSMutableArray* array = [NSPropertyListSerialization
propertyListFromData:data
mutabilityOption:NSPropertyListImmutable
format:&propertyListFormat
errorDescription:nil];
However, the documentation mentions that the above method is obsolete and you should use propertyListWithData:options:format:error: instead.
However, it's much easier to just call NSArray's initWithContentsOfFile: method instead:
NSString *plistPath = [[NSBundle mainBundle] pathForResource...
NSArray *array = [[NSArray alloc] initWithContentsOfFile:plistPath];
if (array) {
//your existing code here...
}
else {
NSLog(#"Plist does not exist");
}
[array release];

Related

How to add null or nil value in "dynamic NSArray" in Objective-C

I am facing a problem regarding to add Null or nil value in NSArray. Actually I am adding Null value because my array count is not same. I am adding three array in Custom TableViewCell two array are from webservice and one array is from database. I am saving IndexPath in core data and then retrieving it.
As shown in Image I am saving indexPath in String and convert it in NSInteger from DidSelectAtIndexPath and show it in cellForRowAtIndexPath. My problem is, it's getting override because it is stored in string. So that I save it in coredataa and retrieve it but I get problem for mismatch count of array in cellforrowatindexpath. My code is like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *STI=#"STI";
AuditTableViewCell *cell = (AuditTableViewCell *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:STI];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"AuditTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
cell.accessoryType=UITableViewCellAccessoryNone;
}
cell.audittitlelbl.text=[NSString stringWithFormat:#"%#",[idarray objectAtIndex:indexPath.row]];
cell.auditdesclbl.text=[NSString stringWithFormat:#"%#",[namearray objectAtIndex:indexPath.row]];
NSManagedObject *device2 = [devices objectAtIndex:indexPath.row];
NSMutableArray *Checkarray=[devices valueForKey:#"Key"]; // Hear in this array I am getting Index count problem
NSLog(#"Device =%#",device2);
NSLog(#"Check Array =%#",Checkarray);
if(indexPath.row == CurrentIndexPath)
{
cell.listlbl.text=GlobalString;
[cell setBackgroundColor:[UIColor greenColor]];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CurrentIndexPath=indexPath.row;
NSLog(#"Current Index Path =%ld",(long)CurrentIndexPath);
GlobalIndexPath = [[NSString stringWithFormat: #"%ld", (long)CurrentIndexPath] mutableCopy];
NSManagedObjectContext *context = [self managedObjectContext];
if (self.device) {
// Update existing device
[device setValue:GlobalStringChk forKey:#"Key1"];
[device setValue:GlobalIndexPath forKey:#"Key"];
} else {
// Create a new device
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:#"Device" inManagedObjectContext:context];
[newDevice setValue:GlobalStringChk forKey:#"Key1"];
[newDevice setValue:GlobalIndexPath forKey:#"Key"];
}
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[Audittable reloadData];
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Device"];
self.devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
}
This is my code hear I am saving indexpath in coredata and retrieving it in array. I have a problem for count of array in cellforrowatindexpath. How can I add Null or nil value in array so that it count get same. My Array is a dynamic array.The main problem is that I need to change the colour of cell if user click on it.when I store the value in NSInteger and convert it into indexpath I can change the colour But only for one cell at a time.When I click on other cell integer value get override. So for that I save it to core data and retrive but when I fetch core data array in cellforrowatindexpath it crash because of different count .Thanks in Advance!
We cannot add nil directly inside a collection like NSMutableArray as it will raise an exception. If at all it is required to add nil inside collections like NSMutableArray, we can do so by using a singleton class NSNull.
For instance, we have a array of type NSMutableArray, we can add nil using NSNull as-
[array addObject:#"string"];
[array addObject:[NSNull null]];
...
and so on...
The NSNull class defines a singleton object used to represent null values in collection objects (which don’t allow nil values).
First you can't add elemets in NSArray, you must use NSMutableArray instead. Second thing you can not add nil in your NSMutableArray.
So, you should add empty string instead of nil something like,
NSMutableArray *arr = [[NSMutableArray alloc]init];
[arr addObject:#""];
or you can add NSNull Object like,
[arr addObject:[NSNull null]];
And you can check that string is empty or not when want to display in UI something like(if added empty string),
NSString *tempStr = [arr objectAtIndex:0];
if (tempStr.length > 0) {
NSLog(#"string is not empty.");
}
else{
NSLog(#"Sring is empty");
}
Ok I got your problem. You actually wants to merge data from 3 different array into one single cell, but the problem is your array count is different for each array. Yes you can do this but you have to change the way of coding.
Lets assume you have 3 arrays a1, a2 and a3, with counts:
a1.count = 5,
a2.count = 8,
a3.count = 10
Here I'm assuming a1 is array of NSString while a2 & a3 are arrays with numbers (NSIntegers)
Now create one simple class inheriting from NSObject. We call it as AuditTableViewDao. It is simple Data Access Object
#import <UIKit/UIKit.h>
#interface AuditTableViewDao : NSObject
#property (nonatomic) NSString *checkForWhat;
#property (nonatomic) NSString *methodMeasures;
#property (nonatomic) NSString *visualInspections;
- (instancetype)initWithCheckForWhat:(NSString *)checkForWhat methodMeasures:(NSString *)methodMeasures visualInspections:(NSString *)visualInspections;
#end
Then in .m file of this AuditTableViewDao.m, write implementation to initialize.
Then in your ViewComtroller.m file, create one property
#property (nonatomic) NSMutableArray *arrayAuditTableViews;
in viewDidLod, write
self.arrayAuditTableViews = [[NSMutableArray alloc] init];
for (NSInteger index=0; index < a3.count; index++) {
AuditTableViewDao *auditTableViewDao = nil;
if (index < a1.count) {
auditTableViewDao = [[AuditTableViewDao alloc] initWithCheckForWhat:((NSString *)[a1 objectAtIndex:index]) methodMeasures:[NSString stringWithFormat:#"%d", [a2 objectAtIndex:index]] visualInspections:[NSString stringWithFormat:#"%d", [a3 objectAtIndex:index]]];
}
else if (index < a2.count) {
auditTableViewDao = [[AuditTableViewDao alloc] initWithCheckForWhat:"" methodMeasures:[NSString stringWithFormat:#"%d", [a2 objectAtIndex:index]] visualInspections:[NSString stringWithFormat:#"%d", [a3 objectAtIndex:index]]];
}
else if (index < a3.count) {
auditTableViewDao = [[AuditTableViewDao alloc] initWithCheckForWhat:"" methodMeasures:"" visualInspections:[NSString stringWithFormat:#"%d", [a3 objectAtIndex:index]]];
}
[self.arrayAuditTableViews addObject:auditTableViewDao];
}
And finally in your cellForRowAtIndexPath, Use object of arrayAuditTableViews at perticular index path.
You need to add null in your array like this:
[yourArray addObject:[NSNull null]];
Add null
[array addObject:[NSNull null]];
nil is not an object that you can add to an array: An array cannot
contain nil. This is why addObject:nil crashes.
Don't need to save the index Path in core data and don't need to add Null or nill value.
Just alloc NSMutableArray in viewDidLoad method
NSMutableArray *_selectedIndexPath = [[NSMutableArray alloc] init];
Then in didSelectRowatIndexPath addObject in array like this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (![_selectedIndexPath containsObject:indexPath]) { // Check if array does not contain selecting cell, add this cell to array
[_selectedIndexPath addObject:indexPath];
[tableView reloadData];
}
}
And then in Cellforrowatindexpath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([_selectedIndexPath containsObject:indexPath]) {
cell.backgroundColor=[UIColor greenColor];
}
else
{
cell.backgroundColor=[UIColor whiteColor];
}
}
And you can change your colour without using core data and null or nil value.

Coding error prevents NSMutableArray from returning data after ALAssetsLibrary query

I've written the following code (iOS 6) to try to recover the list of photos from an album (albumName) using ALAssetLibrary. The code compiles and according to the static analyzer I don't have memory leaks or other issues, but the NSLog statement. The code successfuly finds the photo album and if I break inside the loop I can see that the data are being written to the Dictionary. But they are not being emplaced as objects in the Array!
I am sure this is an oversight on my part but I am not able to spot it. Any help appreciated, and maybe the correct answer will help someone else. AlAssets has been difficult for me!
Thanks in advance
TF Redfield
(NSMutableArray )retrieveImageNames: (NSString)albumName
{
ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
NSMutableArray* assetGroups = [[NSMutableArray alloc] init];
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop)
{
if (group)
{
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop)
{
if (asset)
{
//compare the names of the albums
if ([albumName compare: [group valueForProperty:ALAssetsGroupPropertyName]]==NSOrderedSame)
{
NSMutableDictionary *workingDictionary = [[NSMutableDictionary alloc] init] ;
[workingDictionary setObject:[asset valueForProperty:ALAssetPropertyType] forKey:#"UIImagePickerControllerMediaType"];
[workingDictionary setObject:[UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage]] forKey:#"UIImagePickerControllerOriginalImage"];
[workingDictionary setObject:[[asset valueForProperty:ALAssetPropertyURLs] valueForKey:[[[asset valueForProperty:ALAssetPropertyURLs] allKeys] objectAtIndex:0]] forKey:#"UIImagePickerControllerReferenceURL"];
[assetGroups addObject:workingDictionary];
//If I place the log here I get data
//NSString * temp = [NSString stringWithFormat:#"%d", [assetGroups count]];
//NSLog(#"%#",temp);
[workingDictionary release];
}
}
}];
}
} failureBlock:^(NSError *error)
{
NSLog(#"error enumerating AssetLibrary groups %#\n", error);
}];
NSString * temp = [NSString stringWithFormat:#"%d", [assetGroups count]];
NSLog(#"%#",temp);
return assetGroups;
[assetsLibrary release];
[assetGroups release];
}
enumerateGroupsWithTypes is an asynchronous call. You have returned your array before it gets filled.

Accessing an array by index passed by function call

I have written a specific init-override-function where i want to pass an index number to be called in an array. The index number itself is defined by selecting a tablerow in a tableview by the user. So.. the rownumber that is selected shall be passed into the init function and used there for further processing.
Well.. now there is my problem.. In my opinion the methods I've created are all correctly coded. But when I click my defined Connect Button an error message appears in the console, that, the index is out of bounds. So.. I've checked the array for entries and there are all available. So the indexnumber should be fine.
Maybe fyi: I've created a copy of the array in the TableViewController that is originally located in the PortConnection file.
Here are the necessary files. Can anyone give me a helping hand, where to search for?
PORTTABLEVIEWCONTROLLER.M
- (IBAction)pushConnect:(id)sender {
NSInteger selectedRow = [tableView selectedRow];
[portConnection initPort:selectedRow];
}
- (id)init {
self = [super init];
if (self) {
// Initialization of port Connection instance
portConnection = [[PortConnection alloc] init];
// Fill array in Portconnection.m with devices
[portConnection listDevices];
// Fill tableView Data Array with data from portConnection array
self.tableViewDataArray = [NSMutableArray arrayWithArray:portConnection.portArray];
}
return self;
}
PORTCONNECTION.H
#
interface PortConnection : NSObject {
// Instance of AMSerialPort
AMSerialPort *port;
// Port Array to be filled with found ports
NSMutableArray *portArray;
}
// List Devices into an given array
- (void)listDevices;
// Connect to selected port
- (void)initPort:(NSInteger)selectedRow;
#property (nonatomic, retain) NSMutableArray *portArray;
#property (nonatomic, retain) AMSerialPort *port;
#end
PORTCONNECTION.M
#implementation PortConnection
#synthesize port;
#synthesize portArray;
#pragma mark -
#pragma mark Serial Port Access
- (void)listDevices {
// get an port enumerator
NSEnumerator *enumerator = [AMSerialPortList portEnumerator];
AMSerialPort *aPort;
while ((aPort = [enumerator nextObject])) {
[portArray addObject:[PortItem portItemWithTitle:[aPort name] andPath:[aPort bsdPath]]];
}
}
- (void)initPort:(NSInteger)selectedRow {
//Create object of selected port searched in array
PortItem *portSelected = [portArray objectAtIndex:selectedRow];
NSString *deviceNameSelected = [portSelected valueForKey:#"bsdPath"];
// Start Connection
if (![deviceNameSelected isEqualToString:[self.port bsdPath]]) {
[self.port close];
[self setPort:[[[AMSerialPort alloc] init:deviceNameSelected withName:deviceNameSelected type:(NSString *)CFSTR(kIOSerialBSDModemType)] autorelease]];
[self.port setDelegate:self.port];
if ([self.port open]) {
NSLog(#"Connected...");
[self.port setSpeed:B38400];
[self.port readDataInBackground];
} else {
NSLog(#"error connecting");
[self setPort:nil];
}
}
}
#pragma mark -
#pragma mark initialization / deallocation
- (id)init {
self = [super init];
if (self) {
portArray = [NSMutableArray array];
}
return self;
}
- (void)dealloc {
portArray = NULL;
[super dealloc];
}
#end
Well.. my idea is, that something is wrong with the method INITPORT:(NSINTEGER)SELECTEDROW
but I am not sure at all....
Thanks so much for giving me advice again!
Sebastian
Your problem is this line,
portArray = [NSMutableArray array];
Although it is a retained variable, it will retain the value when you use the property setter method. This is a direct assignment of an autoreleased object will be released in a while and baring any other object retaining it (which doesn't happen), it should get deallocated. This is something which you don't want. Remedy this by using the property setter,
self.portArray = [NSMutableArray array];

Insert Array in SQLite database?

Okay,
I am a noob and want to get a array from my server and insert it into a SQLite database table. My code is below. Please help!
- (void)getAlbums {
// Get Albums
NSString *userId;
userId = #"1";
NSString *post =[NSString stringWithFormat:#"userid=%#", userId];
NSString *hostStr = #"********************************************?";
hostStr = [hostStr stringByAppendingString:post];
NSData *dataURL = [NSData dataWithContentsOfURL: [ NSURL URLWithString: hostStr ]];
NSString *serverOutput = [[NSString alloc] initWithData:dataURL encoding: NSASCIIStringEncoding];
//Create Array
NSArray *myWords = [serverOutput componentsSeparatedByString:#" "];
// Output server response
NSLog(serverOutput);
//Initialize the array.
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
listOfItems = [[NSArray arrayWithObjects: myWords, nil] retain];
NSString *test;
while (*test in listOfItems) {
[sqlite executeNonQuery:#"INSERT INTO photo_albums VALUES (?, ?);", variableOne, variableTwo];
}
}
there is more than one issue in this code.
NSMutableArray *listOfItems = [[NSMutableArray alloc] init];
this will leak in the next line because you reassign another item to the variable. Change it into:
NSArray *listOfItems;
listOfItems = [[NSArray arrayWithObjects: myWords, nil] retain];
this does not what you think it does. It will initialize an array with the array myWords as a "member", it won't add the objects from myWords.
But why does this line exist anyway? myWords is already an array. And the retain which doesn't get released would be another leak.
NSString *test;
while (*test in listOfItems) {
this is just wrong. use:
for (test in listOfItems) {
[sqlite executeNonQuery:#"INSERT INTO photo_albums VALUES (?, ?);", variableOne, variableTwo];
where the f do you get variableOne and variableTwo from? And why do you loop through your listOfItems if you don't use the NSString named test?
}
no problem with the closing bracket.
Oh and serverOutput doesn't get released too, so there is another leak. And I'm pretty sure that the encoding isn't ascii.
Start over with the iOS 101, learn the basics and ignore sqlite for the next 2 weeks.

Obj-C methods: calling with parameters

I've been using C-style functions, but I just learned they can't see instance variables. So I was advised to convert them to Objective-C methods.
NSString* myfunc ( int x )
becomes:
- (NSString *)myfunc:(int)x
and
myString = myfunc(x);
becomes
myString = [myString myfunc:x];
??
This compiles with ominous warnings, but does not work. What have I misunderstood?
It looks like your call is incorrect. Perhaps try:
NSString *myString = [self myfunc:x];
As far as I understand, you send the -myfunc: message to a NSString object. So the -myfunc: method should be declared in NSString class (or a category of NSString).
If this is what you want to do, you don't need to return the object itself as the result of the method: you can modify its instance variables in the method implementation. The method call (or message sending) looks like:
[myString myfunc:x];
If you want to send the message to an object of another class and return a string, your method declaration is correct but must appear in your class implementation and the receiver of the message (this is the item on the left in the square brackets) must be of that class:
#implementation MyClass
-(NSString *)myfunc:(int)x
{
NSString * returnString;
...// do something with x, returnString and instance variables
return returnString;
}
#end;
...
MyClass * myobj = [[MyClass alloc] init];
NSString * myString = [myobj myfunc:42];
As a second answer, I am trying to understand your problem through all your recent questions.
At the beginning, there was a C function returning a pointer to a NSString object:
NSString * myfunc( int x )
{
... // Do something with x
NSString * myString = #"MYDATA";
... // Do something with myString
return myString;
}
Then, you wanted to add in that function some code about an UIImage object:
image1.image = [UIImage imageNamed:#"image1.png"];
You were advised to convert the function to a method. If you want to access .image instance variable, this method has to belong to the class of image1 object (let's say this is AlanImage class). Something like this:
#interface AlanImage : NSObject {
UIImage image;
}
- (NSString *) myfuncWithParam: (int) x;
#end;
#implementation AlanImage
- (NSString *) myfuncWithParam: (int) x
{
NSString * myString = #"MYDATA";
image = [UIImage imageNamed:#"image1.png"];
return myString;
}
#end
Third, you didn't know what was the receiver of the method. My investigations tend to lead to your image object as a good candidate:
aNiceString = [image myfunc:aNiceInteger];
Finally (this question), not getting a satisfying answer, you reworded your third question, with success this time as it happens.
I am curious to get a more complete view of your project in order to give you some hints. Anyway, it seems that you are learning Objective-C and object oriented concepts: congratulations and stay motivated!
You haven't worked out what Object Oriented Programming is. With [theObject method] you can only call methods belonging to the specific instance.
I am not sure that following trick correctly work for a "general" objective-c, but in apple implementation you can do such:
#interface SomeClass: NSObject {
int m_someVariable;
...
};
- (NSString *) someMethod;
...
#end
#implementation SomeClass
...
//pure c function with extra one parameter
//for accessing to instance variables
static NSString privatePlainCeeMethod(SomeClass *my, int fortyTwo) {
NSString *str;
//access to a instance variable as for a usual
//cee structure field: my->fieldName
...
return [NSString stringWithFormat:#"someVariable:%d, fortyTwo:%d",
my->m_someVariable, fortyTwo];
};
- (NSString *) someMethod {
...
return privatePlainCeeMethod(self,42);
};
...
#end
I use such trick to divide a big objc method on observable private simple functions. These functions (a) do not pollute class interface and (b) are invoked faster than objc method.

Resources