iPad iOS4 dictionary count returns 0 - objective-c

I am checking that the entries in my dictionary are not 0 using NSDictionary count. It works and returns the correct number except on iPad 4.3 Simulator and iPads running iOS 4.3.
Is this a known bug in iOS 4 or am I seeing a side effect of something else I am doing which is iOS 4 incompatible?
edit:
Thank you for your comments so far! I am happy to believe that it's my code; I'm new to this. Here is a greatly simplified version of my code.
-(NSDictionary *)dictionaryOfSets
{
if (!_dictionaryOfSets)
{
NSOrderedSet* set1 = [[NSOrderedSet alloc] initWithObjects:
[NSNumber numberWithInt:(1)],
[NSNumber numberWithInt:(2)],
[NSNumber numberWithInt:(3)],
[NSNumber numberWithInt:(4)],
nil];
NSOrderedSet* set2 = [[NSOrderedSet alloc] initWithObjects:
[NSNumber numberWithInt:(9)],
[NSNumber numberWithInt:(10)],
[NSNumber numberWithInt:(11)],
nil];
_dictionaryOfSets = [[NSDictionary alloc] initWithObjectsAndKeys:
set1, [NSNumber numberWithInt:(1)],
set2, [NSNumber numberWithInt:(2)],
nil];
[set1 release];
[set2 release];
}
return _dictionaryOfSets;
}

I would double check that the dictionary isn't nil. Messaging nil in that case - [dict count] - would just do that - return 0.

After a few weeks of searching and tests I have concluded that NSOrderedSet does not work under iOS 4.3. The same code works under iOS 5.0 and 5.1. I have replaced the NSOrderedSet with NSSet and it now works under iOS 4.3. I am surprised that I haven't found documentation covering that.

Related

NSMutableDictionary won't save data

hope someone can help me with a problem I've been wrestling with...
Using MapBox to develop a map-based app, and I want to attach an NSMutableDictionary to each of the map annotations to store additional data. I had it working but XCode kept throwing me warning about some of my data/object types, so I went through and tidied those up, and now it's broken. The idea is that on ViewDidLoad, the program runs through a set of plist dictionaries to set up each annotation correctly - that's still running okay, because my initial anno markers pop up with their correct settings. However rather than run back to the plist every time, I want to attach a dictionary to each annotation's userinfo property, which I can then use for toggling selection data and other functions. Here's my code:
NSDictionary *ExploreSteps = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ExploreSteps" ofType:#"plist"]];
for (NSString *key in [ExploreSteps allKeys])
{
//Loop through keys for each anno
NSDictionary *thisStep = [ExploreSteps objectForKey:key];
NSNumber *annoIndex = [thisStep objectForKey:#"Index"];
NSNumber *isLive = [thisStep valueForKey:#"isLive"];
NSString *annoTitle = [thisStep objectForKey:#"Title"];
NSString *annoText = [thisStep objectForKey:#"Text"];
NSString *imagefile = [thisStep objectForKey:#"Imagefile"];
double longitude = [[thisStep objectForKey:#"Longitude"] doubleValue];
double latitude = [[thisStep objectForKey:#"Latitude"] doubleValue];
NSString *pagefile = [thisStep objectForKey:#"Pagefile"];
NSString *audiofile = [thisStep objectForKey:#"Audiofile"];
CLLocationCoordinate2D annoCoord = CLLocationCoordinate2DMake(latitude, longitude);
RMAnnotation *annotation = [[RMAnnotation alloc] initWithMapView:mapView coordinate:annoCoord andTitle:annoTitle];
annotation.annotationIcon = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:imagefile ofType:#"png"]];
annotation.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:annoIndex, #"index", isLive, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
NSLog(#"Title: %#",[annotation.userInfo objectForKey:#"annoTitle"]);
[mapView addAnnotation:annotation];
}
The NSLog should spit out the annoTitle string, but instead it's giving me a null every time, and the behaviour of the rest of the app also shows that info stored in the dictionary simply isn't "getting through".
Any ideas? Thanks in advance!
ETA: Modified code for initializing the dictionary (not that it seems to make any difference to the problem!):
NSMutableDictionary *myUserInfo = [[NSMutableDictionary alloc] initWithObjectsAndKeys:annoIndex, #"index", isLive, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
annotation.userInfo = myUserInfo;
NSLog(#"Title: %#",[annotation.userInfo objectForKey:#"annoTitle"]);
NSLog(#"Length: %u",[[annotation.userInfo allKeys] count]);
(Title now returns "(null)", while Length returns "1", if that's at all helpful...)
Almost certainly one of your objects is nil. You mention that allKeys] count] returns 1 so I can go further and say that your value for isLive is nil. Hence your original line:
[NSMutableDictionary dictionaryWithObjectsAndKeys:annoIndex, #"index", isLive, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
Acts exactly the same as:
[NSMutableDictionary dictionaryWithObjectsAndKeys:annoIndex, #"index", nil, #"isLive", annoTitle, #"annoTitle", annoText, #"annoText", imagefile, #"imagefile", pagefile, #"pagefile", audiofile, #"audiofile", nil];
And the dictionary takes annoIndex to be the final key-value pair.
I'd suggest that probably you want to take a mutable copy of thisStep and strip out the keys you don't want, then pass it along as the userInfo.
It's the way you are creating the NSMutableDictionary for userInfo. Take a look at this Difference between [[NSMutableDictionary alloc] initWithObjects:...] and [NSMutableDictionary dictionaryWithObjects:...]?
"
+dictionaryWithObjects: returns an autoreleased dictionary
-initWithObjects: you must release yourself
if you want the dictionary to persist as a instance variable, you should create it with an init method or retain an autoreleased version, either way you should be sure to release it in your dealloc method
"

AQRecorder AAC play in AVPlayer

I have some problem with recorded aac audio file on iOS.
I need record audio and play it with AVPlayer. Recording work fine, file created. i can play it on Mac. But when i try play it(file) in AVPlayer - no sound.
property for record audio
mRecordFormat.mSampleRate = 8000;
mRecordFormat.mFormatID = kAudioFormatMPEG4AAC;
mRecordFormat.mFormatFlags = kAudioFormatMPEG4AAC_LD;
mRecordFormat.mFramesPerPacket = 0;
mRecordFormat.mChannelsPerFrame = 1;
mRecordFormat.mBitsPerChannel = 0;
mRecordFormat.mBytesPerPacket = 0;
i try different setting but no results. Help please
P.S. Other files(mp3 etc) playing normaly
Ok, i find solution couple years ago.
I change record logic to
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithFloat:16000], AVSampleRateKey,
[NSNumber numberWithInt:2], AVNumberOfChannelsKey,
[NSNumber numberWithInt:AVAudioQualityMedium], AVSampleRateConverterAudioQualityKey,
[NSNumber numberWithInt:64000], AVEncoderBitRateKey,
[NSNumber numberWithInt:8], AVEncoderBitDepthHintKey,
nil];
error = nil;
_recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
and this recorded file play on iOS, Android and MacOS/Win. So it's all.

Populate an NSArray via an NSDictionary which is populated via a .plist

Here is the code:
-(void)viewDidLoad {
[super viewDidLoad];
//Verb data read, sorted and assigned to a dictionary
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"VerbDictionary" ofType:#"plist"];
NSDictionary *verbDictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
NSArray *verbs = [verbDictionary allKeys];
NSArray *vSorted = [verbs sortedArrayUsingSelector:#selector(compare:)];
NSString *selectedVerb = [vSorted objectAtIndex:0];
NSArray *vArray = [verbDictionary objectForKey:selectedVerb];
self.verbArrayData = [[NSArray alloc] initWithArray:vArray];
}
Here is a screenshot of the Error message I'm getting:
(from https://plus.google.com/u/0/113629344949177123204/posts/SwHzXL6kvvJ)
The self.verbArrayData is not populating from the vArray. self.verbDataArray is nil and it shouldn't be.
I have tried this from scratch and I have done this before, actually, in the past, but via iOS 4 and Release/retain memory management. This is the first pure iOS 5 ARC app I have started.
Any ideas?
I figured it out... But I am not sure why it works. Self.verbArrayData is invalid. However, just verbArrayData works just fine. So, I changed self.verbArrayData = [[NSArray...] TO just verbArrayData = [[NSArray...] and it compiled and ran fine. So, self.verbArrayData is the getter not the setter in this case; I think. THanks - –

IOS Category on NSArray Causes "instance message does not declare a method with selector"

I am trying to use the Category described in this article:
http://iphonedevelopment.blogspot.com/2008/10/shuffling-arrays.html
I have setup the following:
// NSArray+Shuffle.h
#import <Foundation/Foundation.h>
#interface NSArray (Shuffle)
-(NSArray *)shuffledArray;
#end
// NSArray+Shuffle.m
#import "NSArray+Shuffle.h"
#implementation NSArray (Shuffle)
-(NSArray *)shuffledArray
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
NSMutableArray *copy = [self mutableCopy];
while ([copy count] > 0)
{
int index = arc4random() % [copy count];
id objectToMove = [copy objectAtIndex:index];
[array addObject:objectToMove];
[copy removeObjectAtIndex:index];
}
// Using IOS 5 ARC
// [copy release];
return array;
}
#end
Then in my code that I want to use this, I imported the Category:
#import "NSArray+Shuffle.h"
Then, I attempted to use it like this:
NSArray *orderedGallary = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic1", #"pageName",
[UIImage imageNamed:#"Pic1.jpg"],#"pageImage",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic2", #"pageName",
[UIImage imageNamed:#"Pic2.jpg"],#"pageImage",
nil],
nil];
NSArray *shuffler = [[NSArray alloc] shuffledArray:orderedGallary];
_pageData = [shuffler shuffledArray:orderedGallary];
But, I get the following compiler error message:
ModelController.m: error: Automatic Reference Counting Issue: Receiver type 'NSArray' for instance message does not declare a method with selector 'shuffledArray:'
Any ideas?
shuffledArray is a method that takes no parameters, it is different from shuffledArray:, which is a method that takes one parameter.
It looks like what you meant was:
NSArray* shuffled = [orderedGallery shuffledArray];
Here you are sending this message to your original array, and it returns a new array that is shuffled.
You're trying too hard. You only need to send -shuffledArray to orderedGallery.
NSArray *orderedGallary = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic1", #"pageName",
[UIImage imageNamed:#"Pic1.jpg"],#"pageImage",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Pic2", #"pageName",
[UIImage imageNamed:#"Pic2.jpg"],#"pageImage",
nil],
nil];
_pageData = [orderedGallery shuffledArray];
See how you have declared shuffledArray not to take any arguments? Simply sending this message to any instance of NSArray will return your shuffled array.
shuffledArray does not take a parameter but is called directly on the array:
NSArray *myShuffledArray = [orderedGallery shuffledArray]
You have declared (in the .h) and defined (in the .m) a method named shuffledArray.
You are calling a method named shuffledArray: (notice the colon, which indicates an argument).
You want to be calling
NSArray *shuffled = [orderedGallery shuffledArray];
you don't need the argument because you are sending the method to the ordered array.
(There's no object that's actually a "shuffler" - independent of the array - so I wouldn't use that name as the variable name. The array is shuffling a copy of itself and returning the new shuffled array.)

NSString EXC_BAD_ACCESS

I'm stuck with the following bit of code.
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
When I log gridRef, it displays the correct result. However, the line setting answerLabel.text causes an EXC_BAD_ACCESS error and the program crashes. IB is connected to the correct label, what is the problem?
Thanks
I've updated the code as follows:
- (IBAction)convertLatLong {
NSArray *latLong = [[NSArray alloc] initWithObjects: latTextField.text, longTextField.text, nil];
GridRefsConverter *converter = [[GridRefsConverter alloc] init];
NSString *gridRef = [[NSString alloc] initWithFormat: #"%#", [converter LatLongToOSGrid: latLong]];
NSLog(#"Grid Ref: %#", gridRef);
NSLog(#"Label: %#", self.answerLabel.text);
answerLabel.text = #"Yippy";
self.answerLabel.text = gridRef;
[gridRef release];
[converter release];
[latLong release];
}
answerLabel is initialised through #property #synthesize when the view controller is pushed onto the stack. (I don't know how it gets init'd apart from it's one of the magical things IB does for you. Or so I assume. I've used exactly the same method in other view controllers and have not had this issue.
I've found the culprits - the question is, how do I go about releasing them?
NSString *eString = [[NSString alloc] initWithFormat: #"%f", e];
NSString *nString = [[NSString alloc] initWithFormat: #"%f", n];
eString = [eString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
nString = [nString stringByPaddingToLength: (digits/2) withString: #"0" startingAtIndex: 0];
NSString *theGridRef = [letterPair stringByAppendingString: eString];
theGridRef = [theGridRef stringByAppendingString: nString];
[eString release];
[nString release];
return theGridRef;
and:
NSArray *gridRef = [[NSArray alloc] init];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: E]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithDouble: N]];
gridRef = [gridRef arrayByAddingObject: [NSNumber numberWithInteger: 8]];
NSString *theGridRef = [[NSString alloc] initWithFormat: #"%#", [self gridRefNumberToLetter: gridRef]];
[gridRef release];
[theGridRef autorelease];
return theGridRef;
}
You should enable zombie detection by setting the environment variable NSZombieEnabled to YES, so you can see which object causes the bad access (don't forget to remove this again when you found the bug).
Also you can use Instruments to find the location where the object actually gets released. For this start a new Instruments session and use the "Allocations" instrument. In the instrument settings check "Enable NSZombie detection" and "Record reference counts". When running the session you will break where the error occurs and you see a record of all retains/releases.
One place where you can have a quick look if your object is incorrectly freed is in the -viewDidUnload method, where you should release the outlet and set it to nil. If you forget the latter and you access the outlet somehow, it will result in a EXC_BAD_ACCESS.
Edited to match your update:
The problem is that you are assigning eString (and nString) a new string which was alloc/init-ed. Then you override those in the next statements, because -stringByPaddingToLength: (as well as all the other -stringBy... methods) return a new and autoreleased string object. So you lost the reference to the old string which means that there is a memory leak. Additionally at the end you release the already autoreleased objects explicitly which causes your bad access.
Instead you should create autoreleased strings from the beginning ([NSString stringWithFormat:...]) and don't release them at the end.
Check if asnwerLabel is actually non-null. You should also change this line:
self.answerLabel.text = [[NSString alloc] initWithFormat: #"%#", gridRef];
To:
self.answerLabel.text = [NSString stringWithFormat: #"%#", gridRef];
Otherwise, you will end up with a memory leak in that line.
Maybe the label is not inited at that point in your code, try to check it. Why are you allocating a new NSString?
Just do:
self.label.text = gridRef;
[gridRef release];
how is answerLabel created? You might need to retain that. Or you possibly need to release some things (gridRef)?
I can't see any other issues with your code.
You can (and probably should) set your
answerLabel.text = gridRef;
gridRef is already an NSString, so you don't need to alloc it again.
EXC_BAD_ACCESS is usually a memory thing related to your retain/release count not balancing (or in my extensive experience of it :p).
Okay, the problem was trying to release NSStrings, so I've stopped doing that and the problem has been solved.
Can someone clarify how strings are retained and released. I was of the impression that:
string = #"My String"; is autoreleased.
NSString *string = [[NSString alloc] init...]; is not autoreleased and needs to be done manually.