strange behavior with postNotificationName with NSDictionary in userInfo - ios6

I'm trying to debug a strange problem I'm seeing when passing a NSDictionary as my userInfo in NSNotification using ARC on ios 6. Here's my relevant code:
to send the notification:
NSDictionary *feedData = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
NSDictionary* finalData = [[NSDictionary alloc] initWithObjectsAndKeys:#"index", [NSNumber numberWithInt:i], #"myKey", feedData, nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object: nil userInfo:finalData];
my handler registration:
[[NSNotificationCenter defaultCenter] addObserver:frcvc selector:#selector(mySelector:) name:#"myNotification" object:NULL];
my handler declaration:
-(void)mySelector:(NSNotification*)notification;
my handler definition which is inside my MyCollectionViewController class:
- (void)mySelector:(NSNotification*) notification
{
NSDictionary* myDict = (NSDictionary*)notification.userInfo;
int index = [[myDict objectForKey:#"index"] integerValue];
NSDictionary* myFeed = (NSDictionary*)[myDict objectForKey:#"myKey"];
}
When I run the code, I can see finalData being constructed, and I make note of the memory address. When I get to my callback and extract userInfo, it is the same memory address, but xcode seems to think it is of type MyCollectionViewController*. And when I try to access index or myFeed, it is null. But in my output window, I can type
"p myDict"
and it correctly shows NSDictionary* with the same memory address as when constructed! And if I type
"po myDict"
it shows the correct dictionary keys and values! What is going on?! I've tried rebuilding clean, but that didn't help. The memory seems to be fine, since I'm getting the correct address and can seem to access it in the debug window. Thanks for any help!

Your objects and keys are in the wrong order:
NSDictionary* finalData = [[NSDictionary alloc] initWithObjectsAndKeys:#"index", [NSNumber numberWithInt:i], #"myKey", feedData, nil];
It should be:
NSDictionary* finalData = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:i], #"index", feedData, #"myKey", nil];
Or even better, using Objective-C Literals:
NSDictionary* finalData = #{#"index": [NSNumber numberWithInt:i], #"myKey": feedData};

Related

Call an array from object

-(NSMutableArray *) botanicalPlant {
if (_botanicalPlant == nil) {
_botanicalPlant = [[NSMutableArray alloc] initWithObjects:#"Abelia", nil];
}
return _botanicalPlant;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.botanicalPlantName = [[BotanicalPlant alloc] init];
self.botanicalPlantNameLabel.text = []
}
I know this may be a simple question but Im stuck on this one. I have an array in NSObject of Botanical I just don't know how to call it in the viewDidLoad method for it to show up in my main view controller. I don't know what to put in the brackets to assign it to the text of the label.
You haven't shown enough code to answer your question for sure. But if botanicalPlant is a mutable array property on the BotanicalPlant class, then you could do something like:
BotanicalPlant *myBotanicalPlant = [[BotanicalPlant alloc] init];
NSMutableArray *namesArray = myBotanicalPlant.botanicalPlant;
self.botanicalPlantNameLabel.text = [namesArray firstObject];
Try This:
In your viewDidLoad method
NSMutableArray *arrayFromBotanicalMethod = [self botanicalPlant];
// Then set the text label from your new array that you created
self.botanicalPlantNameLabel.text = [arrayFromBotanicalMethod objectAtIndex:0];
This code is based on the limited information you provided. You basically create another array that will hold the returned array from your botanicalPlant method. Then you use the objectAtIndex message on the new array to get the text for the label.
Hope it helps.

Accessing a variable in multiple methods

Still a bit new and I am having some issues I was hoping someone could help with. I am trying to load a JSON string coming from my server into a collectionview in iOS6
I can pull in the data using a fetchedData method called from the viewDidLoad method and that part works fine. In the fetchedData method, I break out the JSON data and place it in NSDictionaries and NSArrays and can dump the correct data to the log to see it.
The problem is when I try and use any of the information elsewhere in my code such as get the amount of elements in any of hte arrays to use as a counter to fill the collectionview.
It may be that I am tired but I can't seem to get my head around this part. The declaration of many of the main variables was in the fetchedData method and I thought since the were declared there it could be the reason I could not see them elsewhere so I moved the declaration of the variables to the interface section and was hoping this would make the variables GLOBAL and the fetchedData method continues to work just fine, but nowhere else.
When I put in breaks in the cell definition area I can see in the debugger window the variables come up as empty.
I am not sure what sections of the code you may want to see so let me know and I can post them but maybe someone could give an example of how arrays and dictionary items can be accessed in multiple methods.
To avoid confusion and to expose my hodgepodge of code at this point anyway here is the .m file or at least most of it Please don't rip to hard on the coding style I have been trying anything I could think of and tore it up pretty hard myself and it was late.
#import "ICBCollectionViewController.h"
#import "ICBCollectionViewCell.h"
#import "ICBDetailViewController.h"
#interface ICBCollectionViewController () {
NSDictionary* json;
NSDictionary* title;
NSDictionary* shortDescrip;
NSDictionary* longDescrip;
NSDictionary* price;
NSDictionary* path;
NSDictionary* sKU;
NSDictionary* audiotrack;
NSDictionary* audiotracksize;
NSArray* titles;
NSArray* shortDescription;
NSArray* longDescription;
NSArray* prices;
// NSArray* paths;
NSArray* SKUs;
NSArray* audiotracks;
NSArray* audiotracksizes;
}
#end
/*
#interface NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress;
-(NSData*)toJSON;
#end
#implementation NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress
{
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString: urlAddress] ];
__autoreleasing NSError* error = nil;
id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
-(NSData*)toJSON
{
NSError* error = nil;
id result = [NSJSONSerialization dataWithJSONObject:self options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
#end
*/
#implementation ICBCollectionViewController
#synthesize paths;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: imobURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
// Do any additional setup after loading the view.
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
//parse out the json data
json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
titles = [json objectForKey:#"title"]; //2
shortDescription = [json objectForKey:#"shortD"];
longDescription = [json objectForKey:#"longD"];
prices = [json objectForKey:#"price"];
self.paths = [json objectForKey:#"path"];
SKUs = [json objectForKey:#"SKU"];
audiotracks = [json objectForKey:#"audiotrack"];
audiotracksizes = [json objectForKey:#"audiotracksize"];
NSLog(#"paths: %#", paths); //3
// NSLog(#"shortDescrip: %#", shortDescription);
NSInteger t=7;
// 1) Get the latest loan
title = [titles objectAtIndex:t];
shortDescrip = [shortDescription objectAtIndex:t];
longDescrip = [longDescription objectAtIndex:t];
price = [prices objectAtIndex:t];
path = [paths objectAtIndex:t];
sKU = [SKUs objectAtIndex:t];
audiotrack = [audiotracks objectAtIndex:t];
audiotracksize = [audiotracksizes objectAtIndex:t];
//NSLog(title.count text);
//NSLog(title.allValues);
// 2) Get the data
NSString* Title = [title objectForKey:#"title"];
NSString* ShortDescrip = [shortDescrip objectForKey:#"shortD"];
NSString* LongDescrip = [longDescrip objectForKey:#"longD"];
NSNumber* Price = [price objectForKey:#"price"];
NSString* Path = [path objectForKey:#"path"];
NSString* SKU = [sKU objectForKey:#"SKU"];
NSString* AudioTrack = [audiotrack objectForKey:#"audiotrack"];
NSNumber* AudioTrackSize = [audiotracksize objectForKey:#"audiotracksize"];
/*************************HERE THE DATA EXISTS*******************************/
/******** Path = "XYXYXYXYXYXY" for example ********************************/
// 3) Set the label appropriately
NSLog([NSString stringWithFormat:#"Here is some data: Title: %# Path %# SKU: %# Price: %# Track %# Size %#",Title, Path, SKU, Price, LongDescrip, AudioTrackSize]);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//DetailSegue
if ([segue.identifier isEqualToString:#"DetailSegue"]) {
ICBCollectionViewCell *cell = (ICBCollectionViewCell *)sender;
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
ICBDetailViewController *dvc = (ICBDetailViewController *)[segue destinationViewController];
dvc.img = [UIImage imageNamed:#"MusicPlayerGraphic.png"];
}
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
NSLog(#"paths qty = %d",[paths count]);
return 20;
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier=#"Cell";
ICBCollectionViewCell *cell = (ICBCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
// paths = [json objectForKey:#"path"];
NSDictionary* path = [paths objectAtIndex:indexPath.row];
NSString* Path = [path objectForKey:#"path"];
// NSString* Path = [paths objectAtIndex:indexPath.row];
NSLog(#"%d",indexPath.row);
/***********************HERE IT DOES NOT**************************/
/******** Path = "" **********************************************/
NSLog(#"xxx");
NSLog(path);
NSLog(paths);
NSLog(Path);
NSLog(#"ZZZ");
Path=#"deepsleep";
NSLog(#"xxx");
NSLog(Path);
NSLog(#"ZZZ");
// paths = [json objectForKey:#"path"];
// NSString* Path = [path objectForKey:#"path"];
NSString *imagefile = [NSString stringWithFormat:#"https://imobilize.s3.amazonaws.com/glennharrold/data/%#/mid.png", Path];
NSLog(imagefile);
NSURL *url1=[NSURL URLWithString:imagefile];
dispatch_async(kBgQueue, ^{
NSData *data1 = [NSData dataWithContentsOfURL:url1];
cell.imageView.image =[[UIImage alloc]initWithData:data1];
});
return cell;
}
#end
Try breaking out the JSON data and sorting it in the appDelegate. If you declare public variables there #property (nonatomic, strong) NSDictionary *myDict etc., then you can access those variables by importing your appDelegate and using the following code:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSDictionary *newDict = appDelegate.myDict;
Otherwise, you can store the information in a singleton, or in the root view controller. The key is to store your variables in a class that won't be deallocated. Most often, it is a bad idea to use a viewController for that purpose-- they have a tendency to be navigated away from, which deallocates the memory and gets rid of your variables. Google "model-view-controller" for more info.
I found out what the main issue was it the ViewDidLoad method I was using a background activity to get the JSON data from my server and as that process was running the foreground was also being processed and since the rest of the code was based on a value returned when the background process finished the data was actually null so all data based on that single piece were also null and it looked as if it was not available. Once I made the process run in the foreground all the variable started having values.
Thanks for your assistance with this

CoreData leaks with fetched results arrays

I'm using CoreData to store objects like cars, trips, data recorded from GPS, etc.
When I fetch what I want to show a list of trips, some stats for a trip, or add a new car in my settings view controller, I use pretty much this kind of request:
- (void)getDataTrip
{
// Fetched data trips.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DataTrip" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
// Set predicate and sort orderings...
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"idTrip = %#", self.idTrip];
[fetchRequest setPredicate:predicate];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
NSLog(#"failed with error: %#", error);
}
// Set the array.
[self setDataTripArray:mutableFetchResults];
// Memory management.
[fetchRequest release];
[mutableFetchResults release];
}
Sometimes, I have leaks when I do the [self setDataTripArray:mutableFetchResults]; and sometimes not. In this case, when I get the data for a trip, it leaks all the time when I use the navigation controller to come back to the root view controller (displaying a list of trips), and/or when I change tab.
Anyway, it just leaks and it's all the time coming from fetching data from CoreData, and give this array to my local array var.
Please let me know if you see how to fix this! It made the app crash after a while.
Thanks!
SOLUTION
I found that I do a retain on my object dataTripArray object when creating another UIViewController that I use to create graphs for my scroll view.
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0)
return;
if (page >= kNumberOfPages)
return;
// Replace the placeholder if necessary.
GraphController *controller = [self.graphControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
controller = [[GraphController alloc] initWithPageNumber:page data:[self.dataTripArray retain]];
[self.graphControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// Add the controller's view to the scroll view.
if (controller.view.superview == nil)
{
CGRect frame = _scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[self.scrollView addSubview:controller.view];
}
}
I just removed the retain and the leak is no longer coming up. Fixed!

Saving arrays of iOS Apps

I would like to know how to save arrays of my iOS app. I used a method, but it only saved on my iPod Touch. I'd tested on iPad and it didn't work.
What I need is a code to save my app array on any device...
Anyone can help me?
Here is the code:
myAppViewController.h:
- (NSString *)GetApplicationDocumentsDirectory;
- (void)applicationWillTerminate:(NSNotification *)notification;
myAppViewController.m:
-(NSString*) GetApplicationDocumentsDirectory {
static NSString* documentsDirectory = nil;
if (documentsDirectory == nil) {
documentsDirectory = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES)
objectAtIndex:0] retain];
}
NSString *fullFileName = [NSString stringWithFormat:#"%#/TarefasSalvas.plist", documentsDirectory];
return fullFileName;
}
- (void)applicationWillTerminate:(NSNotification *)notification{
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:lisTitulos];
[array addObject:lisImagens];
[array addObject:lisData];
[array addObject:lisDetalhes];
[array writeToFile:[self GetApplicationDocumentsDirectory] atomically:YES];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSString *filePath = [self GetApplicationDocumentsDirectory];
//if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
if (array != nil){
lisTitulos = [array objectAtIndex:0];
lisImagens = [array objectAtIndex:1];
lisData = [array objectAtIndex:2];
lisDetalhes = [array objectAtIndex:3];
}
else {
NSDate *date = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setLocale:[NSLocale currentLocale]];
[dateFormat setDateFormat:#"dd/MM/YYYY"];
NSString *dateNow = [dateFormat stringFromDate:date];
lisTitulos = [[NSMutableArray alloc] initWithObjects:#"Titulo",nil];
lisImagens = [[NSMutableArray alloc] initWithObjects:#"0.png",nil];
lisData = [[NSMutableArray alloc] initWithObjects:dateNow,nil];
lisDetalhes = [[NSMutableArray alloc] initWithObjects:#"",nil];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
}
Thanks
It is possible that you're just sending the app to the background and not terminating them. On such occasion, applicationWillTerminate: will not be called. That seems to be the likely cause as the rest of the code looks fine.
And your GetApplicationDocumentsDirectory seems a bit strangely named as it gets the path to the file in which the array is stored. In addition to that,
NSString *fullFileName = [NSString stringWithFormat:#"%#/TarefasSalvas.plist", documentsDirectory];
is not the correct way of getting the file path. You should use thestringByAppendingPathComponent: method on documentsDirectory like this,
NSString *fullFileName = [documentsDirectory stringByAppendingPathComponent:#"TarefasSalvas.plist"];
It would also make sense to declare fullFileName as the static variable rather than documentsDirectory.
Thanks #Deepak.
After u giving me the hint about making the Log Alerts on Debugging, I noticed that the iPad is killing the App and then the app can't call the applicationWillTerminate: method as you mentioned before.
So I solved the Problem changing my code to:
- (void)applicationWillTerminate:(NSNotification *)notification{
[self SavingStatus];
}
-(void)SavingStatus {
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:lisTitulos];
[array addObject:lisImagens];
[array addObject:lisData];
[array addObject:lisDetalhes];
[array writeToFile:[self GetApplicationDocumentsDirectory] atomically:YES];
}
As you can see, I created a method that I call on applicationWillTerminate: method and everytime the app reload the Table data:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
...
[self SavingStatus];
return cell;
}
By this way, even if your device Kill the App before it Close normally, the informations will be saved anyway =D.
Thanks again to #Deepak
and everybody who replied me.

Dynamically created UIView Objects

I have a predicament, in as much that I need to create an arbitrary amount of UIView Objects. I have an NSArray and what I need to do is create UIView Objects for the number of items in the array, so I got an int from the [NSArray count]; method, so I know the number of objects needing creating, but the way to implement this has me stumped. I'll include some psudocode below to attempt to give across what I need to do:
[UIView returnMultipleUIViewsForInt:[theArray count]];
Obviously that won't work, but some way of creating an arbitrary amount of objects at runtime, which I can work with would be good.
So in short:
I need to create a certain number of UIViews based upon the number of items in an array.
I then need to access each view that is created and use it as a regularly created view might be used, doing things like adding one of them as a subview to a different view.
- (NSArray *)createNumberOfViews:(NSInteger)number
{
NSMutableArray *viewArray = [NSMutableArray array];
for(NSInteger i = 0; i < number; i++)
{
UIView *view = [[UIView alloc] init];
// any setup you want to do would go here, e.g.:
// view.backgroundColor = [UIColor blueColor];
[viewArray addObject:view];
[view release];
}
return viewArray;
}
NSMutableArray *newViews = [NSMutableArray array];
for (int i=0; i<[theArray count]; ++i) {
UIView *view = [[UIView alloc] init];
[newViews addObject:view];
[view release];
}

Resources