Filled NSArray unintentionally loses its content - objective-c

An array is filled properly but when I try to access to content again, seems that is empty again!! trying to post as many code as necessary. Thanks for help.
I declare appData in .h file:
#interface userViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *appData;
IBOutlet UIActivityIndicatorView *activityIndicator;
IBOutlet UILabel *status;
IBOutlet UITableView *mainTable;
}
#property (nonatomic, retain) NSArray *appData;
#property (nonatomic, retain) UIActivityIndicatorView *activityIndicator;
#property (nonatomic, retain) UILabel *status;
#property (nonatomic, retain) UITableView *mainTable;
- (IBAction) refresca: (id)sender;
- (void)getData: (NSString *)usuari: (NSString *)clau;
#end
in .m file is synthesized and released. appData is properly filled when connection request ends and then, when I reload tableview, when numberOfRowsInSection executed (while loop also in order to test), appData is empty!
- (void)requestFinished:(ASIHTTPRequest *)request {
NSLog(#"%#", [request responseString]);
self.appData = [[request responseString] componentsSeparatedByString: #"#"];
int x =0;
while (x<[appData count] - 1)
{
NSLog(#"Aplicaciones = %#",[appData objectAtIndex: x]);
x = x+1;
}
[activityIndicator stopAnimating];
activityIndicator.hidden = YES;
status.hidden = YES;
mainTable.hidden = NO;
[mainTable reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([appData count] != 0)
{
NSLog(#"Counter = %#",[appData objectAtIndex:0]);
}
return [appData count]-1;
}

A couple of things here.
Your test logging in numberOfRows will cause the app to hang if appdata ever has a non-zero count.
Are you sure you are getting the the same appData object that you populate in requestFinished ?
I suggest using the accessor in numberOfRows as in [self.appData count] which might sort out the problem.
And is there a specific reason you subtract one from the count? As you will lose one element from the array in the tableView that way

Related

How do i play many sounds selected by picker?

I want to make an app playing sound selected by picker.
When i select a file by picker, if i touch button, the sound will play.
But, it cannot.
I cannot point the miss of the code.
Anyone can help me to troubleshoot my code?
Here is my code for troubleshooting.
.H file
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController : UIViewController <
UIPickerViewDelegate,UIPickerViewDataSource,AVAudioPlayerDelegate> {
NSArray *ap;
NSString *si;
UIButton * utab;
AVAudioPlayer *apr;
}
#property (weak, nonatomic) IBOutlet UILabel *ll;
#property (weak, nonatomic) IBOutlet UIImageView *igv;
#property (weak, nonatomic) IBOutlet UIPickerView *pv;
#property (weak, nonatomic) NSArray *msa;
#end
.M file
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#interface ViewController ()
#property AVAudioPlayer *apr;
#end
#implementation ViewController
#synthesize ll,igv,pv;
- (void)viewDidLoad
{
[pv setDelegate:self];
[pv setDataSource:self];
[ll setText:[ap objectAtIndex:0]];
ap = [NSArray arrayWithObjects:#"AAA",#"BBB",#"CCC",#"DDD",#"EEE", nil];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#.m4a",[[NSBundle mainBundle] resourcePath]]];
NSError *error;
self.apr = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
apr.numberOfLoops = 2;
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [ap count];
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent: (NSInteger)component {
return [ap objectAtIndex:row];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent: (NSInteger)component {
si = [ap objectAtIndex:row];
[ll setText:si];
NSString *in = [NSString stringWithFormat:#"%#.jpg",si];
UIImage *tig = [UIImage imageNamed:in ];
[igv setImage:tig];
}
- (IBAction)utabu {
[self.apr play];
}
#end
Most likely the sound file could not be loaded. You should check the error variable after initializing the AVAudioPlayer.
The code for creating the URL objects is also wrong. You should use:
[[NSBundle mainBundle] URLForRessource:<filename> withExtension:"m4a"]
Also you would have to create a new AudioPlayer each time a sound file is selected.
Next time please use better variable names and not just two or three letter garbage.

search through data in multiple arrays

im doing a searchbar for my UITableview.
So far its only filtering one array in - (void) searchthroughdata, namely the self.Title.
But I want it to filter through two arrays - the self.Title and self.Description.
My .h file:
#property (nonatomic, strong) NSArray * Images;
#property (nonatomic, strong) NSArray * Title;
#property (nonatomic, strong) NSArray * Description;
#property (nonatomic, strong) NSMutableArray *results;
#property (nonatomic, strong) IBOutlet UISearchBar *SearchBar;
My .m file:
-(void)searchThroughData {
self.results = nil;
NSPredicate *resultsPredicate = [NSPredicate predicateWithFormat:#"SELF contains [search] %#", self.SearchBar.text];
self.results = [[self.Title filteredArrayUsingPredicate:resultsPredicate] mutableCopy];
}
-(void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self searchThroughData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.tableView) {
return _Title.count;
} else {
[self searchThroughData];
return self.results.count;
}
// Return the number of rows in the section.
//return _Title.count;
}
How do I make it filter through the NSArray * Description as well?
Your best option would be to not have multiple arrays. Instead, create a custom object with your
#property (nonatomic, strong) NSArray * Images;
#property (nonatomic, strong) NSArray * Title;
#property (nonatomic, strong) NSArray * Description;
(or a dictionary) and have a single array containing those objects (I am assuming something about your data model here...).
Now, when you filter you can just check each item in the predicate with an OR.

Which method is better / cleaner?

While looping the
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {}
method im setting data to my custom cell (an image and a text). I was wondering what's the best way to achieve this.
I can use 2 different array (image array and text array) and do something like:
cell.image setImage: //get image from imageArray with indexPath.row
cell.text.text = [textArray objectAtIndex:indexPath.row];
or use a multi dimensional array like this:
cell.image setImage: [imageArray objectAtIndex:indexPath.row] objectAtIndex:0;
cell.text.text = [textArray objectAtIndex:indexPath.row] objectAtIndex:1;
// or use of dictionary with keys
What method is quicker or more readable?
Personally I think the following is the cleanest solution:
Create a model for the items in your array.
Create an UITableViewCell subclass to display the model in the cell. The subclass will have a property that accepts the model and redraw itself when the model changes.
Let's say we have a news app. The array is filled with items for which we create the model NewsItem. The model header could look like this:
NewsItem.h
#interface FSNewsItem : NSObject <NSCoding>
#property (nonatomic, copy, readonly) NSString *title;
#property (nonatomic, copy, readonly) NSURL *URL;
#property (nonatomic, copy, readonly) NSString *time;
#property (nonatomic, copy, readonly) NSString *points;
#property (nonatomic, copy, readonly) NSString *author;
// initialiser
+ (FSNewsItem *)newsItemWithTitleNode:(HTMLNode *)node
subTextNode:(HTMLNode *)subNode;
#end
Now for the cell we create a NewsItemCell. The code for NewsItemCell might look like the following:
NewsItemCell.h
#interface FSNewsItemCell : UITableViewCell
#property (nonatomic, strong) FSNewsItem *newsItem;
#end
NewsItemCell.m
#interface FSNewsCell ()
#property (nonatomic, strong) UILabel *titleLabel;
#property (nonatomic, strong) UILabel *detailLabel;
#end
#implementation FSNewsItemCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
self.titleLabel = [[UILabel alloc] init];
[self addSubview:_titleLabel];
self.detailLabel = [[UILabel alloc] init];
[self addSubview:_detailLabel];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
const CGRect bounds = self.bounds;
CGFloat width = bounds.size.width - (FS_FLOAT_PADDING * 2) - 15.0f;
_titleLabel.frame = CGRectMake(FS_FLOAT_PADDING, FS_FLOAT_CELL_PADDING_VERTICAL, width, 20.0f);
CGFloat y = _titleLabel.frame.size.height + (FS_FLOAT_CELL_PADDING_VERTICAL * 2);
_detailLabel.frame = CGRectMake(FS_FLOAT_PADDING, y, width, 15.0f);
}
#pragma mark - Private
- (void)setNewsItem:(FSNewsItem *)newsItem
{
if (_newsItem == newsItem)
{
return;
}
_newsItem = newsItem;
self.titleLabel.text = newsItem.title;
if (_newsItem.points && _newsItem.time)
{
self.detailLabel.text = [NSString stringWithFormat:#"%# | %#", _newsItem.points, newsItem.time];
}
else
{
self.detailLabel.text = newsItem.time;
}
[self setNeedsLayout];
}
Finally, when we want to display the news item in the cell, our code would look like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
FSNewsItemCell *cell = (FSNewsItemCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[FSNewsItemCell alloc] initWithStyle:UITableViewCellStylePlain reuseIdentifier:CellIdentifier];
}
cell.newsItem = [_items objectAtIndex:indexPath.row];
return cell;
}
This is in my opinion the cleanest solution and the approach I take most of the time. I like to keep my view controllers small. I also like the fact that my view controller doesn't have to know which controls my (custom) table view cell has. The table view cell gets full responsibility on how to draw itself depending on the data supplied.
I would add a simple data class and put class instances in the NSArray. Or use a NSArray of NSDictionary objects so the items can be addressed by name, not position.
Example class code (classes are very easy these days):
#interface DisplayItems : NSObject
#property (nonatomic, strong) NSString *text;
#property (nonatomic, strong) UIImage *image;
#end
#implementation DisplayItems
#end
One idea is to create a custom class:
#interface CellInfo : NSObject
#property (....) UIImage *image;
#property (....) NSString *text;
#end
Then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
...
CellInfo *info = [self.cellInfoArray objectAtIndex:indexPath.row];
cell.image = info.image;
cell.text = info.text;
...
}

Gaining access to an NSMutableArray

I am new to arrays and objects. I have a class HowToPlay.h (of course that has a .m also) their i define two arrays
NSMutableArray *nibs;
NSMutableArray *unusedNibs;
#property (nonatomic, retain) NSMutableArray *nibs;
#property (nonatomic, retain) NSMutableArray *unusedNibs;
then we jump into the .m,
i write all the stuff for the arrays,
nibs = [[NSMutableArray alloc]initWithObjects:#"Question 2", #"Question 3", nil];
self.unusedNibs = nibs;
[nibs release];
Then i also have another class called Question 1, i need to be able to use this array in that class, and be able to change it, but keep the changes on the HowToPlay.m file.
here is why, basically this array loads random NIB files, and then deletes them from the array when they have been used.
in Question 1.m here is what im doing to use the array
random = arc4random() % [self.unusedNibs count];
NSString *nibName = [self.unusedNibs objectAtIndex:random];
[self.unusedNibs removeObjectAtIndex:random];
if (nibName == #"Question 3") {
Question_3 *Q3 = [[Question_3 alloc] initWithNibName:#"Question 3" bundle:nil];
Q3.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:Q3 animated:YES];
[Q3 release];
}
if (nibName == #"Question 2") {
Question_2 *Q2 = [[Question_2 alloc] initWithNibName:#"Question 2" bundle:nil];
Q2.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:Q2 animated:YES];
[Q2 release];
}
This all seems to work fine, but the problem im having is it seems that objects in the array are not getting deleted, even though this line runs
[self.unusedNibs removeObjectAtIndex:random];
I haved tried making it
[HowToPlay.unusedNibs removeObjectAtIndex:random];
I get a error saying expected '.' before '.' token
It seems to me that i have access to read the array, but not to change it. any way to fix this so i can change the array? thanks
UPDATE:
here is the whole HowToPlay.h file contents:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
int score;
int usedQ1;
int usedQ2;
int usedQ3;
NSMutableArray *nibs;
NSMutableArray *unusedNibs;
#interface HowToPlay : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
UIPickerView *selectType;
NSMutableArray *selectArray;
AVAudioPlayer *audioPlayer;
UIActivityIndicatorView *progress;
UIButton *worldButton;
UIButton *politicsButton;
UIButton *starButton;
}
#property (nonatomic, retain) NSMutableArray *nibs;
#property (nonatomic, retain) NSMutableArray *unusedNibs;
#property (nonatomic, retain) IBOutlet UIButton *worldButton;
#property (nonatomic, retain) IBOutlet UIButton *politicsButton;
#property (nonatomic, retain) IBOutlet UIButton *starButton;
#property (nonatomic, retain) IBOutlet UIPickerView *selectType;
#property (nonatomic) int usedQ1;
#property (nonatomic) int usedQ2;
#property (nonatomic) int usedQ3;
#property (readwrite) int score;
-(IBAction)World:(id)sender;
- (IBAction)Politics:(id)sender;
-(IBAction)Stars:(id)sender;
#end
#import "MainViewController.h"
#import "Question 1.h"
#import "Question 2.h"
#import "Question 3.h"
I import after because otherwise i get errors
Also I have the array set up on theViewDidLoad part of HowToPlay, is this a bad idea?
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
NSURL *click = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/click.wav", [[NSBundle mainBundle] resourcePath]]];
audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:click error:nil];
audioPlayer.numberOfLoops = 1;
progress = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
progress.frame = CGRectMake(0.0, 0.0, 30.0, 30.0);
progress.center = self.view.center;
[self.view addSubview: progress];
usedQ1 = 0;
usedQ2 = 0;
usedQ3 = 0;
selectArray = [[NSMutableArray alloc]init];
[selectArray addObject:#"World"];
[selectArray addObject:#"Politics"];
[selectArray addObject:#"Stars"];
score = 0;
nibs = [[NSMutableArray alloc]initWithObjects:#"Question 2", #"Question 3", nil];
self.unusedNibs = nibs;
[nibs release];
}
How do you check if objects are actually deleted? Please post some more code as the way you remove objects from unusedNibs seems to be fine. As per this code:
[self.unusedNibs removeObjectAtIndex:random];
and this code
[HowToPlay.unusedNibs removeObjectAtIndex:random];
The reason why you're getting this error is because you're trying to access unusedNibs using a class name "HowToPlay" instead of its instance "self".
A couple of things: first, to call properties like unusedNibs in HowToPlay it looks like you are calling the class and not an instance. You need to create a HowToPlay object and assign it to a property in your Question1 object so the question one Object has something to call to. Question1 should not call 'self' for unusedNibs since it does not own it.
With what you are doing it might make more sense to put the function figuring out the next random controller in the HowToPlay class. That way your question view controllers could just ask it to return which controller is next without having to duplicate that code across every question controller.

Share NSArray Contents between multiple methods in a single class

What am I doing wrong? My code crashes when I try to log the array. Here is my class:
#interface ArrayTestAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
NSArray *array;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) NSArray *array;
-(IBAction)buttonPressed;
#end
#implementation ArrayTestAppDelegate
#synthesize window, array;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
array = [NSArray arrayWithObjects:#"Banana", #"Apple", #"Orange", #"Pear", #"Plum", nil];
[window makeKeyAndVisible];
}
-(IBAction)buttonPressed {
NSLog(#"%#", array);
}
- (void)dealloc {
[window release];
[array release];
[super dealloc];
}
#end
This is a common memory management error in Cocoa. The arrayWithObjects method of the NSArray class returns an autoreleased object. By the time you try to log the array in the buttonPressed method, the array has already been released and you get a crash. The fix is easy:
array = [[NSArray alloc] initWithObjects:#"Banana", #"Plum", nil];
Or:
array = [[NSArray arrayWithObjects:#"Banana", #"Plum", nil] retain];
I guess the first one is better, the retain on the end of the second example is easy to miss. I would suggest that you read some more on memory management in Cocoa.