I have a signature page, were the user can sign their name.
It needs to then be saved to NSDictionary, but i want to call a List of the keys to be text in a TableView for each row or cell.
so:
"viewImage = saved as object to key:Random Number"
That parts somewhat easy, the hard part is when i call it on the other Page to the TableView.
It Exits the App with Error"SIGABRT". Now all my Delegates are in place and working...i believe.
now heres some example code:
FirstPage.m
UIImagePNGRepresentation(viewImage);
NSMutableArray *innerArray = [[NSMutableArray array]init];
[innerArray addObject:viewImage];
[SignatureSave setObject:innerArray forKey:#"5599"];
simple Enough, but doesnt give me an error.
SecondPage.m
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FirstPage *appShare = (FirstPage *)[[UIApplication sharedApplication] delegate];
NSArray *dataDuplicate = [[NSArray alloc]init ];
dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
static NSString *CellIdentifier = #"Cell";
NSLog(#"%#",dataDuplicate);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero]autorelease];
}
if (dataDuplicate != nil) {
cell.textLabel.text = [dataDuplicate objectAtIndex:indexPath.row];
}
else
{
UIAlertView *CellAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Error Loading content, Try Again Later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[CellAlert show];
[CellAlert release];
}
return cell;
}
#end
Now, How do i get the viewImage to save to the NSDictionary, to be able to call it on the SecondPage and display the name of the objects in the TableVIew?
I really don't understand whats your problem exactly.
first of all, is your dictionary a retained object?
//FirstPage.h
#property (nonatomic, retain) NSDictionary *SignatureSave;
//FirstPage.m
#synthesize SignatureSave;
…
UIImagePNGRepresentation(viewImage);
NSMutableArray *innerArray = [NSMutableArray array]; // using "array" is equivalent to alloc-init-autorelease
[innerArray addObject:viewImage];
[self.SignatureSave setObject:innerArray forKey:#"5599"];
// OR setting the array directly:
UIImagePNGRepresentation(viewImage);
NSArray *innerArray = [NSArray arrayWithObject:viewImage];
[self.SignatureSave setObject:innerArray forKey:#"5599"];
// OR even setting the image directly to the dictionary:
UIImagePNGRepresentation(viewImage);
[self.SignatureSave setObject:viewImage forKey:#"5599"];
now if you access the object by writing self. in front it will call the retain and your object will stay alive. Otherwise it would be autoreleased at the end of the method. This will fix the problem that your dictionary is maybe not present/available at table view creation and you don't have to use a singleton.
what are you trying to access with this code?
FirstPage *appShare = (FirstPage *)[[UIApplication sharedApplication] delegate];
with [[UIApplication sharedApplication] delegate] you get your application delegate (obviously). These are the MyAppNameAppDelegate files but you treat it as a FirstPage class.
Just NSLog() to check you get the right class, the one you expect.
NSLog(#"%#", [[[UIApplication sharedApplication] delegate] class]);
here you have a potential leak, you alloc-init but never release it:
NSArray *dataDuplicate = [[NSArray alloc]init ];
dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
furthermore you can simplify it (will be autoreleased):
NSArray *dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
and here you have another issue.
Why do you call all keys for the object #"innerArray"?
you don't have such an object and it's in many more cases wrong. innerArray was your previously named array in FirstPage.m but it is only for you as a developer to remember the variable better. After compilation it will have a cryptic name anyway. You could access your key #"5599" if you like but I don't think you want this. In your case you want to access all keys of the dictionary so simply call:
NSArray *dataDuplicate = [appShare.SignatureSave allKeys];
now you will have an array with all keys of your dictionary and you can access them like you do with objectAtIndex:.
NSString *keyName = [dataDuplicate objectAtIndex:indexPath.row];
cell.textLabel.text = keyName;
id theObject = [appShare.SignatureSave objectForKey:keyName]; // for example the image
Tell me if this solves your problems or tell me how I misunderstood your question.
I found the answer to this to be quit simple actually,
I ended up going with the Singleton Method instead of the Global Variable Method.
Now the Singleton Method looks terrifying but its quit simple, See here.
The main difference i noticed from the singleton method to the global method is,
Global method takes a lot of converting and re-converting.
Though the Singleton Method is working with a single object over many pages or classes.
Now i hope this will better assist people in the future also!
Related
I'm making an ipad web browser where of course I have an history. The UITableView works well, what doesn't work is the method to add new strings at the history array, and it doesn't give to me any error.
Here's the code:
.h
NSMutableArray *HistoryArray;
.m
-(void)viewDidLoad{
HistoryArray = [[NSMutableArray alloc]init];
}
-(void) addStringsToTheHistory{
NSString *firstURL;
[firstURL isEqual: #"http://www.google.com"];
NSString *currentURL;
[currentURL isEqual: field.text];
[HistoryArray addObject: #"http://www.google.com"];
if ([firstURL isEqual: currentURL]) {
[firstURL isEqual: #"something"];
[TableView reloadData];
}else{
[HistoryArray addObject: currentURL ];
[TableView reloadData];
}
}
Anyone has any idea why it doesn't work well? Is it something about how the HistoryArray is initiated?
The isEqual: method is an object equivalence test and returns a BOOL value. The lines:
NSString *firstURL;
[firstURL isEqual: #"http://www.google.com"];
NSString *currentURL;
[currentURL isEqual: field.text];
don't actually set the variables firstURL and currentURL to anything.
If you want to test for equality between two strings you should use:
if ([firstURL isEqualToString:currentURL]) {
}
If you still don't get the result you expect - set breakpoints or use NSLog functions to inspect the contents of your variables in your viewDidLoad and addStringsToHistory methods.
I'm new to iPhone development and have had great success with with answers from here so I am hoping to receive help directly. I am reading data into a tableview from a plist. The application works fine but I get 2 warnings when I compile. I know why I get the errors but I have been unsuccessful with resolving the issues. Although this app works I really would like to resolve the warnings efficiently. When I tried changing the NSDictionary to NSArray the warning goes away but the table is no longer populated.
Any help would be greatly appreciated.
Staff and Data are defined as NSArray in the Delegate .h file. The warnings show in the delegate .m file below.
My Delegate has the following:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
NSString *Path = [[NSBundle mainBundle] bundlePath];
NSString *DataPath = [Path stringByAppendingPathComponent:#"Data.plist"];
NSString *SPath = [[NSBundle mainBundle] bundlePath];
NSString *StaffPath = [SPath stringByAppendingPathComponent:#"Staff.plist"];
NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
**self.data = tempDict;**
[tempDict release];
NSDictionary *staffDict = [[NSDictionary alloc]initWithContentsOfFile:StaffPath];
**self.staff = staffDict;**
[staffDict release];
In my staff ViewController I have the following:
if(CurrentLevel == 0) {
//Initialize our table data source
NSArray *staffDict = [[NSArray alloc] init];
self.tableDataSource = staffDict;
[staffDict release];
Midwest_DigestiveAppDelegate *AppDelegate = (Midwest_DigestiveAppDelegate *)[[UIApplication sharedApplication] delegate];
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
}
else
self.navigationItem.title = CurrentTitle;
An NSArray holds a one dimensional list of items where an NSDictionary maps keys to values.
Array:
[a, b, c]
Dictionary:
{#"a" = #"first item", #"b" = #"second item"}
Could you declare data as NSDictionary *data; and populate it as data = [[NSDictionary alloc] initWithContentsOfFile:DataPath];
You then access values in the dictionary with [data valueForKey:#"key"]
Everything in your code suggests that the staff and data properties are NSDictionary instances. You initialize them to dictionary objects and you reference them as dictionary objects. Why then are you declaring them as NSArray objects?
You should change how they are declared so they are NSDictionary in your header file rather than NSArray. That seems to me the most logical way to remove your warnings.
This should still work assuming the contents of your "staff" NSDictionary has a key named "Rows" whose value is an NSArray. The code you have to initialize self.tableDataSource with an empty NSArray seems redundant, as you immediately overwrite the value with the
self.tableDataSource = [AppDelegate.staff valueForKey:#"Rows"];
line in your code
This problem might be related to Instance variable not retaining value in iOS application.
My application has a table view that's supposed to be populated with data from a plist. The plist is question is determined based on a defaults setting.
After setting the contents of the instance variable, a count on it shows that it contains multiple records. However, if I then later try to count this instance variable when drawing the table view cells, it shows no records.
--
I declared a "providerData" NSDictionary instance variable in my class header:
#import <UIKit/UIKit.h>
#interface ViewController : UITableViewController {
NSDictionary *providerData;
}
#property (nonatomic, retain) NSDictionary *providerData;
- (void)loadProviderDataSource:(NSString *)providerName;
#end
In my implementation I use viewDidLoad to read a dictionary from a plist file, check the value of "provider_preference", and then call [self loadProviderDataSource:providerPreference]:
- (void)viewDidLoad
{
[super viewDidLoad];
// Retrieve the user's provider preference from defaults:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// Set a default value for provider preference if the user didn't explicitly set one:
NSString *providerPreference = [[NSString alloc] initWithString:#"foo"];
if ([defaults stringForKey:#"provider_preference"]) {
providerPreference = [defaults stringForKey:#"provider_preference"];
}
// Load provider data from the relevant plist:
[self loadProviderDataSource:providerPreference];
}
This all works fine (using NSLog I can see that the plist file is read correctly and that a correct providerPreference value is passed to loadProviderDataSource.
The implementation for loadProviderDataSource is as follows:
- (void)loadProviderDataSource:(NSString *)providerName
{
// Set the filename and path for the data source:
NSString *dataSourceFileName = [[NSString alloc] initWithFormat:#"data-%#.plist",providerName];
NSString *dataSourcePath = [[NSBundle mainBundle] bundlePath];
dataSourcePath = [dataSourcePath stringByAppendingPathComponent:dataSourceFileName];
// Read the provider data:
self.providerData = [[NSDictionary alloc] initWithContentsOfFile:dataSourcePath];
NSLog(#"provider data entry count 1: %d", [self.providerData count]);
}
At this point the NSLog shows me that there are 2 entries in providerData.
However, when I have to set the cells for my table view, the NSLog in the following code shows me that providerData has 0 entries:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"provider data entry count 2: %d", [self.providerData count]);
return cell;
}
In loadProviderDataSource I've tried using:
self.providerData = [[NSDictionary alloc] initWithContentsOfFile:dataSourcePath];
and:
[self setProviderData:[[NSDictionary alloc] initWithContentsOfFile:dataSourcePath]];
... neither work and I still end up with this in my log:
provider data entry count 1: 2
provider data entry count 2: 0
provider data entry count 2: 0
Any ideas?
Based on NSLog(#"%#", self); in both the method which shows up as follows
loadProviderDataSource
self: <ViewController: 0x83739b0>
tableView:cellForRowAtIndexPath
self: <ViewController: 0x8373170>
It looks like you are creating a second instance of ViewController somehow.
I understand one of them is created using XIB. Verify whether you are creating another one programmatically.
I've been attempting to create my own app for a band by my friends, and I've been experimenting with using a custom TableViewCell for news articles that appear on the website. My main objective is to get all of the data from the website, store it in a NSMutableArray, and then display that in my custom cells.
The app runs fine when it loads the first 5-6 cells. However, when I begin to scroll, the app crashes. I've pinpointed the in the cellForRowAtIndexPath: method. Using GDB, I've also come to find out that after I create my NSMutableArray data and once the program runs, after scrolling, my array seems to be autoreleased. I'm not sure why this happens. Here's what I have for my code thus far:
In HomeViewController.h:
#interface HomeViewController : UIViewController {
NSArray *results;
NSMutableArray *titles;
NSMutableArray *dates;
NSMutableArray *entries;
}
#property (nonatomic, retain) NSMutableArray *titles;
#property (nonatomic, retain) NSMutableArray *dates;
#property (nonatomic, retain) NSMutableArray *entries;
#end
In HomeViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
titles = [[NSMutableArray alloc] init];
dates = [[NSMutableArray alloc] init];
entries = [[NSMutableArray alloc] init];
while((i+1) != endIndex){
NSString *curr_title = [[NSString alloc] init];
NSString *curr_date = [[NSString alloc] init];
NSString *curr_entry = [[NSString alloc] init];
//do some character iterations across a string
[titles addObject:curr_title];
[dates addObject:curr_date];
[entries addObject:curr_entry];
[curr_title release];
[curr_date release];
[curr_entry release];
}
}
//more code here, removed
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"NewsCell";
NewsCell *cell = (NewsCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle]
loadNibNamed:#"NewsCell"
owner:self options:nil];
// cell = [topLevelObjects objectAtIndex:0];
for(id currentObject in topLevelObjects){
if([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (NewsCell *)currentObject;
break;
}
}
}
NSLog(#"%d", indexPath.row);
NSLog(#"%d", titles.count);
cell.cellTitle.text = [titles objectAtIndex:indexPath.row];
cell.datePosted.text = [dates objectAtIndex:indexPath.row];
cell.preview.text = [entries objectAtIndex:indexPath.row];
return cell;
}
Again, the first 5-6 cells show up. Once I scroll, I tried doing po titles and got this error:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000009
0x011b309b in objc_msgSend ()
I've tried allocating the arrays in initWithNibName: but that didn't seem to do much. I've tried moving all the code in viewDidLoad: and then calling [super viewDidLoad] and it just produced the same results as well. Sorry this is so long, but I figured people needed to see my code.
I don't see anything obviously wrong with the code you posted. Try enabling the NSZombieEnabled environment variable. This prevents objects from being released so that when your application "crashes" you can determine which object caused the problem.
Also, instead of looping through array of objects returned by loadNibNamed:owner:options, you should assign the desired object to an IBOutlet property of your class. See Loading Custom Table-View Cells From Nib Files for an example.
The following is extracted from the new code you posted:
NSString *curr_title = [[NSString alloc] init];
//do some character iterations across a string
[titles addObject:curr_title];
[curr_title release];
NSString is not mutable (as is NSMutableString). Are you intentionally adding empty strings to the titles array?
In response to your comment: stringByAppendingString creates a new autoreleased NSString object.
NSString *curr_title = [[NSString alloc] init];
// this leaks the original NSString object (curr_title no longer points to it);
// curr_title now points to a new, autoreleased NSString
curr_title = [curr_title stringByAppendingString:#"..."];
[titles addObject:curr_title];
// releasing the autoreleased NSString will cause your application to crash!
[curr_title release];
*EXC_BAD_ACCESS* is a sure sign that one of your objects is getting over released (or wasn't retained properly). NSZombieEnabled is your friend here, just as titaniumdecoy suggests - figuring out what object is being over released is half the battle. Just be sure to turn it off before releasing the app, because (as titaniumdecoy pointed out) it prevents objects from getting released.
I usually use a combination of NSZombieEnabled and well placed breakpoints (so I can walk through the code till it crashes) to figure out where the problem is cropping up in the code. Then it's usually a simple matter of backtracking to figure out where the object was over released.
The problem might be with the implementation of NewsCell, all your properties there being retained?
Also, any reason HomeViewController is subclassing UIViewController and not UITableViewController?
And this should work just fine:
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"NewsCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];
This is my first post on stackoverflow.com so please be kind (rewind) ;)
I have a navigation based application whose purpose is to display blog posts (title) in a Table View (with JSON).
The problem I ran into occurred when a cell got out of the screen and then back in.
I was getting a EXC_BAD_ACCESS (because I sent a message to a deallocated instance), so I struggled to understand where it came from and I finally found a solution. But the fact is I don't exactly understand how the problem occurs. That's why I need someone to enlighten me, I think this is fundamental understanding !
When the connection to the JSON web service has finished loading, I parse the JSON code to obtain a list of blog posts (recentPosts), then I create a BlogArticle object for each post (blogArticle), store it in a MutableArray iVar (allEntries) and insert a row in the Table View :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJsonParser *json = [[SBJsonParser new] autorelease];
NSDictionary *recentPostsData = [json objectWithString:responseString error:&error];
[responseString release];
NSArray *recentPosts = [recentPostsData objectForKey:#"posts"];
int i = 0;
for (NSDictionary *post in recentPosts) {
BlogArticle *blogArticle = [[BlogArticle alloc] initWithDictionary:post];
[allEntries insertObject:blogArticle atIndex:i];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationRight];
i++;
}
}
Here's the initialisation of the BlogArticle object which turned to be the origin of the problem :
- (id)initWithDictionary:(NSDictionary *)article
{
if (self = [super init])
{
// title = [[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML] copy];
// title = [[NSString alloc] initWithString:[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML]];
title = [[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML];
}
return self;
}
So every Objective-C programmer who isn't as noobish as me is able to tell that title is never allocated before being assigned. If I uncomment one of the two lines above it will work. The program crashes exactly when I try to initialize a cell with that title variable, here :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"indexPath.row = %i", indexPath.row);
// Configure the cell.
BlogArticle *article = [allEntries objectAtIndex:indexPath.row];
cell.textLabel.text = article.title;
return cell;
}
Now, what I need to understand is why it does compile/work without allocating the iVar and where exactly it causes trouble (or where exactly the content of title is released causing the program to crash).
Any good resource (noob friendly) about memory management in iOS environment would be much appreciated.
Thanks in advance :)
This line
title = [[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML];
is allocating an autoreleased string. Essentially, think of it that an autoreleased string will get released at the end of the method (though it can last longer, it's useful to think of it that way).
You know the string is autoreleased because the name of the method gtm_stringByUnescapingFromHTML does not start with alloc, new, copy or mutableCopy.
You can add retain to this to stop it getting autoreleased:
title = [[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML] retain];
Now you own the string, and it will not get released until you say so.
The best summary I know of is Apple's own documentation here.
Well, the problem is, that you have to initialize your object, if you want to manage the memory of it on your own. Why should you manage now the memory of title?
Quite simple:
Every object reference, that is stored in an Array, Set, Dictionary etc. is managed by the Array, Dictionary and Set.
If you now just use this reference (by writing: "title = ...") in your cell, you will add the reference also to the cell. And now the cell is also responsible for the object-reference. So if the tableView wants to release your cells, which will happen from time to time to save memory, the cell will release your title-object. And this would cause the NSDitionary to be quite sad, since the NSDictionary wants to take care about the objects stored within itself.
So you could write the following in the tableView-method:
cell.textLabel.text = [article.title retain];
Or the commented lines of your own method.
That means, you will "raise" the storage-level of your object up and if it gets released, the storage level itself will be decreased by one.
If the storage-level will reach zero, it will be completely released (that should happen, if your tablecell is released AND your NSDIctionary)
I hope i could help you a bit :)