I'm trying build an app that will work with iTunes. So, I imported the framework into my project and created a small class, just for learning purposes as follows:
-(NSMutableArray *)processMediaItems:(NSArray *)mediaItems {
NSMutableArray *titles = [NSMutableArray new];
if (mediaItems) {
for (ITLibMediaItem *item in mediaItems) {
[titles addObject:[item.title]];
}
}
return titles;
}
Then I decided to create a unit test for it, and did the following:
- (void)testExample {
ITLibMediaItem *mediaItem = [[ITLibMediaItem alloc] init];
//mediaItem.title = #"I Still Haven't Found What I'm Looking For";
NSMutableArray *mediaItems = [NSMutableArray new];
[mediaItems addObject:mediaItem];
MyClass *mc = [MyClass new];
NSMutableArray *titles = [mc processMediaItems:mediaItems];
}
The problem is that the property title is readonly.
So my question is, how do I create a new ITLibMediaItem object with a title? I tried to read the API (ITLibMediaItem) but didn't found a initialization that could help...
I find it. Properties in ITLibMediaItem are filled using setValuesForKeysWithDictionary:
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
[properties setObject:#"I Still Haven't Found What I'm Looking For" forKey:#"title"];
ITLibMediaItem *mediaItem = [[ITLibMediaItem alloc] init];
[mediaItem setValuesForKeysWithDictionary:properties];
NSMutableArray *mediaItems = [NSMutableArray new];
[mediaItems addObject:mediaItem];
If I have an nsarray full of custom objects and I make a second array using:
NSArray *temp = [NSArray arrayWithArray:original];
then work with some properties of the objects inside the original array, then decide to roll back, I am then using the reverse:
original = [NSArray arrayWithArray:temp];
I am finding the objects I changed in the array also effected my temp array. I also tried implementing copyWithZone on my custom class, and using copyItems and it did not help. What else should I try?
To be clear, in order to use copyWithZone, I changed my array creation command to:
NSArray *temp = [[NSArray alloc] initWithArray:original copyItems:YES];
My copyWithZone:
-(id)copyWithZone:(NSZone *)zone{
CustomObject *ret = [[CustomObject allocWithZone: zone] init];
//copy properties
return ret;
}
I'm filling in a singleton object with mock data to spit out in a function that returns the array that all of this data goes into.
#interface menuItem : NSObject {
//Item's Name
NSString *itemName;
//String of Prices - "$4.50" or "$1.50 / $2.50 / $3.50"
NSArray *priceList;
//List of Ingredients
NSArray *ingredientList;
//List of adjusted Quantities
NSArray *ingredientQuants;
//Number of Sizes for corresponding prices
int numOfSizes;
}
- (NSString *)priceText;
#property (nonatomic, copy) NSString *itemName;
#property (nonatomic, copy) NSArray *priceList;
#property (nonatomic, copy) NSArray *ingredientList;
#property (nonatomic, copy) NSArray *ingredientQuants;
#property (nonatomic, assign) int numOfSizes;
#end
The object doesn't care how many objects I put in it with menuItems with values stored in ingredientQuants, but after a number of declarations, it gets sick of letting ones that do have ingredientQuants declared, even with variables that are declared right before I stash them there, and then it decides to EXEC_BAD_ACCESS on main.c
-(NSArray *)returnAllItems {
return items;
}
- (void) loadMenu {
if(categories && items){
return;
}
categories = [[NSArray alloc] initWithObjects:#"Coffee", #"Coffee Alternatives", #"Smoothies", #"Baked Goods", #"Sandwiches",#"Salads", nil];
NSArray *nyQuants = [[NSArray alloc] initWithObjects:#"No", #"Yes", nil];
//NSArray *ynQuants = [NSArray arrayWithObjects:#"Yes", #"No", nil];
NSArray *numQuants = [[NSArray alloc] initWithObjects:#"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"0", nil];
NSArray *espressoIList = [[NSArray alloc] initWithObjects:#"Decaf", #"Iced", #"Soymilk", #"Shots",nil];
menuItem *menuitemOne = [[menuItem alloc] init];
menuitemOne.itemName = #"Regular Coffee";
menuitemOne.priceList = [NSArray arrayWithObjects:#"$1.50",#"$1.95",#"$2.15", nil];
menuitemOne.numOfSizes = 3;
// menuitemOne.ingredientList = [NSArray arrayWithObjects:#"Decaf", #"Iced", nil];
//menuitemOne.ingredientQuants = [NSArray arrayWithObjects:nyQuants, nyQuants, nil];
menuItem *menuItemTwo = [[menuItem alloc] init];
menuItemTwo.itemName = #"Latte";
menuItemTwo.priceList = [NSArray arrayWithObjects:#"$2.55", #"$3.45", #"$3.75", nil];
menuItemTwo.numOfSizes = 3;
menuItemTwo.ingredientList = espressoIList;
menuItemTwo.ingredientQuants = [NSArray arrayWithObjects:nyQuants, nyQuants, nyQuants, numQuants, nil];
menuItem *mocha = [[menuItem alloc]init];
mocha.itemName = #"Mocha";
mocha.priceList = [NSArray arrayWithObjects:#"$3.15",#"$3.95",#"$4.75", nil];
mocha.numOfSizes = 3;
mocha.ingredientList = espressoIList;
//THIS LINE BREAKS THE ENTIRE PROGRAM
NSArray *aaaa = [NSArray arrayWithObjects:#"ASD", #"DFG", nil];
mocha.ingredientQuants = [NSArray arrayWithObjects:aaaa, nil];
//MORE STUFF HAPPENS LIKE ABOVE
items = [NSArray arrayWithObjects:coffeeArray, espressoArray,smoothieArray, bakedArray, sandwichArray,saladArray, nil];
[nyQuants release];
[numQuants release];
[espressoIList release];
NSLog(#"Categories: %d", [categories retainCount]);
NSLog(#"items: %d", [items retainCount]);
NSLog(#"nyQuants: %d", [nyQuants retainCount]);
NSLog(#"numQuants: %d", [numQuants retainCount]);
NSLog(#"espresslist: %d", [espressoIList retainCount]);
}
Then I init this object, grab its member arrays and put them in a viewcontroller:
CBMenu *WholeMenu = [CBMenu sharedInstance];
NSLog(#"MENU");
NSArray *cats = [WholeMenu returnCategories];
NSLog(#"Cats");
NSArray *menu = [WholeMenu returnAllItems];
NSLog(#"Menu");
//What are these I don't even
[cats retain];
[menu retain];
FoodMenuTwoViewController *mmvc = [[FoodMenuTwoViewController alloc]initWithData:cats :NO: YES];
[mmvc setDataSource:cats];
[mmvc setMenu:menu];
[mmvc setTitle:#"Menu"];
[self.navigationController pushViewController:mmvc animated:YES];
//FOR SOME REASON IT DOESN'T LIKE ME RELEASING CATS AND MENU OR REMOVING THEIR
//RETAIN STATEMENTS ABOVE PLEASE DON'T HURT ME.
return;
When the first bool in the VC is YES, it shows a list of categories. I see this, but when the view appears when it's pushed onto the stack, it proceeds to vomit all of its organs up and die.
NSZombieEnabled tells me CharlieBrown[4285:fb03] * -[__NSArrayI retain]: message sent to deallocated instance 0x6e06550
Set the properties of NSStrings and NSArrays to copy instead of retain and in your init method dont use [[NSArray alloc] initWithObjects:] on your Ingredients List and espressoIList- you dont do it with the other arrays (which is correct). Otherwise you get memory leaks. Use NSArray arrayWithObjects like you did with most of the other arrays.
Edit:
Also change this
menuItemTwo.ingredientList = espressoIList;
To
menuItemTwo.ingredientList = [NSArray arrayWithArray: espressoIList ];
Edit:
Since you say it didn't help you making these adjustments, maybe post the code
you indicate with //more lik this
I don't really see why you get the problem yet. Which is the line causing the crash?
this one?
mocha.ingredientQuants = [NSArray arrayWithObjects:nyQuants, nil];
And if you leave it out it just works?
Edit:
It seems to me nyQuants was released when you tried to add it to the mocha array.
Seems the autoreleasepool gets drained and the object vanishes - though i thought that would only happen after the run loop.
Try to nitialize your nyQuants and other arrays you depend on using alloc init and release them manually after your declaration of all menuItems is done.
I think that should do it!
So, change those
NSArray *nyQuants = [NSArray arrayWithObjects:#"No", #"Yes", nil];
NSArray *ynQuants = [NSArray arrayWithObjects:#"Yes", #"No", nil];
NSArray *numQuants = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"0", nil];
NSArray *espressoIList = [[NSArray alloc] initWithObjects:#"Decaf", #"Iced", #"Soymilk", #"Shots",nil];
All to be alloced like espressoIList is in this example and release them when before your method returns.
If you are getting a NSArray - message sent to deallocated instance, means that you are using a variable with a negative retain count, you deleted/released it somehow.
In the header file you declared your arrays with (nonatomic, retain), it means that their retain count is 1. In the m file you should deallocate the arrays in the - (void)dealloc{} method. You initialized your arrays with arrayWithObjects method, and based on the apple memory management description, with this method you don't own these object, so you cannot release/delete/remove them.
I'm going to paste the code below, then tell you what I've tried to determine from the code.
If you're reading this quickly, start with the text right above the next code-block.
- (void)tableViewDidLoadModel:(UITableView*)tableView {
self.items = [NSMutableArray array];
self.sections = [NSMutableArray array];
NSMutableDictionary *groups = [NSMutableDictionary dictionary];
for (int c = 0;c < [_contacts.people count];c++) {
NSDictionary *person = [_contacts.people objectAtIndex:c];
NSString *name = [person objectForKey:#"fullName"];
NSString *letter = [name substringToIndex:1];
NSMutableArray *section = [groups objectForKey:letter];
NSLog(#"%# - %# - %#", name, [person objectForKey:#"index"], [person objectForKey:#"abId"]);
if (!section) {
section = [NSMutableArray array];
[groups setObject:section forKey:letter];
}
TTTableItem *item = [ttItem itemWithText:name URL:[person objectForKey:#"index"]];
[section addObject:item];
}
Someone else wrote this block of code. From what I can determine, they're trying to take the user's contacts and fill a TableView.
Now my real question has to do with that last line:
TTTableItem *item = [ttItem itemWithText:name URL:[person objectForKey:#"index"]];
[section addObject:item];
This is using the Three20 framework. Apparently what the previous developer did was use TTTableItem to kind of get a pre-formatted UITableViewCell. (Hopefully I'm thinking right?)
I need to replace this line of code with something normal.
I've thought of just using UITableViewCell, but other than that I'm not really sure how to start?
-tableViewDidLoadModel: is a method that comes from the TTTableViewDataSource protocol, so if you're trying to remove Three20 from your project, you've got more to do than just replace the TTTableItem.
TTTableItem is a subclass of NSObject, and the only thing it seems to add is a userInfo property. To get rid of it, you could start by creating your own class with the same property.
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.)