Mutual changes in two NSMutableArray objects - objective-c

Assume, your .h file looks like this:
#property (retain, nonatomic) NSMutableArray *songs;
- (NSMutableArray *)popularSongs;
- (void)make20PercentDiscountToPopularSongs
Your .m file looks like this:
- (NSMutableArray *)popularSongs
{
NSMutableArray *popularSongs = [NSMutableArray array];
for (Song *song in self.songs) {
if (song.isPopular) {
[popularSongs addObject:song];
}
}
return popularSongs;
}
- (void)make20PercentDiscountToPopularSongs
{
for (Song *song in self.popularSongs) {
song.price = song.price * 0.8;
}
}
The code above adds a 20% discount to popular songs. I recognise that there is a more simplistic way of doing this. You could have the "make20PercentDiscountToPopularSongs" and "popularSongs" function in a single function but let's assume that the code is written the way it is written above.
In the example above, will the line:
song.price = song.price * 0.8;
Actually, make any changes to the object:
#property (retain, nonatomic) NSMutableArray *songs;
Or not? Because, it seems as if the line will change the newly created popularSongs NSMutableArray rather than the songs NSMutableArray, am I right? This problem is bothering me for a lot of time now. I would like to make changes just to the original songs array. To me popularSongs works as if you are storing the pointers to the songs in the songs array.

It changes neither array. It changes some of the objects in the songs array. And as the same objects are in the popularSongs array returned then they are 'changed' too.
This:
[popularSongs addObject:song];
Adds the pointer to the current song into the array. It doesn't create a new copy of the song.

You can create a quick mutable array like this:
NSMutableArray *popularSongs = self.songs.mutableCopy;
Rather than iterating through all the songs to determine if they are popular, you could use a filter predicate at the time you create the array.
NSMutableArray *popularSongs = [[self.songs filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:#"isPopular = %#", #YES]] mutableCopy];
With this one-liner you can avoid your methods altogether ;-)- Not sure what you want, but if you want to alter the original songs, leave out the mutableCopy part.

Related

NSMutableDictionary input into an array is somehow missing info on the way out

I have a mutable array (downloadQueue) containing custom objects (AssetNode) and each object has an NSMutableDictionary as a property.
#interface AssetNode : NSObject {
NSMutableDictionary* allData;
}
#property (nonatomic, retain) NSMutableDictionary* allData;
When I remove an AssetNode from the downloadQueue, the .allData property for the remaining AssetNode objects somehow gets modified and ends up missing some value/key pairs.
-(void)removeAssetNodeFromQueue:(AssetNode*)aNode{
NSMutableArray *temp = [NSMutableArray array];
for (AssetNode* node in downloadQueue)
{
if ([aNode.nodeId isEqualToString:node.nodeId])
{
[temp addObject:node];
}
}
[downloadQueue removeObjectsInArray:temp];
}
Any idea why this happens or how to maintain the integrity of the dictionary info?
Add an NSLog or breakpoint at every point in your code where allData is modified. Log the entire dictionary. I would expect that you have a bug in your code, rather than the NSMutableDictionary accidentally losing your data.
Yeah, retain the dictionary of the object being removed
if ([aNode.nodeId isEqualToString:node.nodeId])
{
[temp addObject:node];
[temp.allData retain];
}
But I suggest storing the allData into another variable, otherwise you'll be leaking memory.
Check out apples Automatic Reference Counting (ARC), this doc is nice:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH
Comment on my answer if you'd like for me to quickly explain retain count to you.
Cheers

Updating contents of class variable NSMutableArray

I'm new to Objective C and iOS development in general, so if I missed some information that needs to be included please let me know and I'll do my best. At the same time, if any of my assumptions are incorrect or if I set this up totally dumb, please don't hesitate to yell at me. Thanks!
I have a class object called feeds. Here's how I initialize it in the .h file:
#interface ClassViewController : ContentViewController <UITableViewDelegate,
UITableViewDataSource> {
NSMutableArray *feeds;
}
#property (nonatomic, strong) NSMutableArray* feeds;
and in the .m:
#dynamic feeds;
I'm trying to get results from JSON and load them up into the feeds. Let's assume that resArr has correct data in it:
NSArray *resArr = [results objectForKey:#"data"];
if([self->feeds count]) {
[self->feeds removeAllObjects];
[self->feeds addObjectsFromArray:resArr];
}
else {
self->feeds = [[NSMutableArray alloc] initWithArray:resArr];
}
Now, this works fine the first time (i.e., the first time I put data into the array), but I get the following error subsequent times: *** -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray
I'm clueless. Any ideas?
Edit: JSON structures
1:
{"code":200,"data":[{"name":"ABM"},{"name":"ACC"}]}
2:
{"code":200,"data":{"100":{"subject":"ABM","title":"Decision Making in Agri-Food"},"130":{"subject":"ABM","title":"Farm Management I"}}}
you should use self.feeds instead of self->feeds, here's a good read Dot (“.”) operator and arrow (“->”) operator use in C vs. Objective-C
are you sure the subsequent times are actually NSArrays?
edit additon
Looks like the first JSon is just a array of names, where's as the second is a dictionary keyed with 100, 200, etc. So the JSON converts this into a dictionary keyed to 100, 200, ...
NSArray* resArr = [results objectForKey:#"data"];
if([self.feeds count]) {
[self.feeds removeAllObjects];
if([resArr isKindOfClass:[NSDictionary class]])
[self.feeds addObjectsFromArray:resArr.allValues];
else
[self.feeds addObjectsFromArray:resArr];
}
else {
self.feeds = [[NSMutableArray alloc] initWithArray:resArr];
}

Remove pointer to object from NSMutableArray while keeping object?

I am trying to remove a pointer in an NSMutableArray that points to an object in another array without deleting the object itself. E.g.:
// In ViewController.m – Code abridged and somewhat simplified
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *objectPool;
#property (nonatomic, strong) NSMutableArray *objectsOwnedByFriend;
#property (nonatomic, strong) NSMutableArray *objectsOwnedByMe;
- (void)transferPointerToObjectFromFriendToMe;
- (void)copyPointerToObjectFromFriendToMe;
#end
#implementation ViewController
#synthesize objectPool = _objectPool;
#synthesize objectsOwnedByFriend = _objectsOwnedByFriend;
#synthesize objectsOwnedByMe = _objectsOwnedByMe;
- (void)setObjectPool:(NSMutableArray *)objectPool
{
_objectPool = objectPool;
}
- (NSMutableArray *)objectPool
{
if (!_objectPool) _objectPool = [[NSMutableArray alloc] initWithArray:self.objects]; // self.objects is a mutable array containing multiple NSObjects
return _objectPool;
}
- (void)setObjectsOwnedByFriend:(NSMutableArray *)objectsOwnedByFriend
{
_objectsOwnedByFriend = objectsOwnedByFriend;
}
- (NSMutableArray *)objectsOwnedByFriend
{
if (!_objectsOwnedByFriend)
{
_objectsOwnedByFriend = [[NSMutableArray alloc] init];
[_objectsOwnedByFriend addObjectsFromArray:self.objectPool];
}
return _objectsOwnedByFriend;
}
- (void)setObjectsOwnedByMe:(NSMutableArray *)objectsOwnedByMe
{
_objectsOwnedByMe = objectsOwnedByMe;
}
- (NSMutableArray *)objectsOwnedByMe
{
if (!_objectsOwnedByMe) _objectsOwnedByMe = [[NSMutableArray alloc] init];
return _objectsOwnedByMe;
}
- (void)transferPointerToObjectFromFriendToMe
{
[self.objectsOwnedByMe addObject:[self.objectsOwnedByFriend lastObject]];
[self.objectsOwnedByFriend removeLastObject];
}
- (void)copyPointerToObjectFromFriendToMe
{
[self.objectsOwnedByMe addObject:[self.objectsOwnedByFriend lastObject]];
}
#end
In the above code, when I use transferPointerToObjectFromFriendToMe, removing the last object removes both the pointer to it in self.objectsOwnedByFriend (as I want) and also the object itself in self.objectPool (which I don't want to happen).
What I would like is an array (self.objectPool) that contains all of the actual objects and then two mutable arrays (self.objectsOwnedByFriend and self.objectsOwnedByMe) that contains pointers to objects in self.objectPool and the ability to add and remove more pointers referencing objects in self.objectPool to self.objectsOwnedByFriend and self.objectsOwnedByMe.
Also, when I use either transferPointerToObjectFromFriendToMe or copyPointerToObjectFromFriendToMe, the object doesn't seem to be added properly, as a subsequent check via self.objectsOwnedByMe.count results in 0 instead of 1.SOLUTION = My lazy instantiation for self.objectsOwnedByMe was missing in my original code :SI was able to check whether self.objectsOwnedByMe was properly created via:
NSLog(#"self.objectsOwnedByMe = %#", self.objectsOwnedByMe);
** My first StackOverflow question! ** I hope I was clear...couldn't find a a similar question so apologies if I missed an old thread. Let me know if you need more info to diagnose. (I am trying to learn Obj-C.)
Typo :P Sorry peeps. In my actual code in Xcode I had:
- (void)setObjectPool:(NSMutableArray *)objectPool
{
_objectPool = objectPool;
}
- (NSMutableArray *)objectPool
{
if (!_objectPool) _objectPool = [[NSMutableArray alloc] initWithArray:self.objects];
return _objectsOwnedByFriend;
}
I think my mistake is super obvious (and if not, the mistake was that my getter for objectPool was returning _objectsOwnedByFriend...copy/paste error that I somehow missed).
Everything works now!
This is very peculiar and confusing code. I suspect the problem is that something is calling one of the setters, -setObjectPool: or -setObjectsOwnedByFriend:, with the array of the other object. Those setters simply make the ivar refer to the object that was passed in. Because of that, they are very prone to lead to objects being shared.
Typically, a property like that would be declared and implemented with copy semantics.
It looks like self.objectsOwnedByMe is never initialized and you are therefore always working with nil instead of an actual NSMutableArray.
Somewhere (perhaps in a custom getter for objectsOwnedByMe as below?) you need to create an array before you start using it:
- (NSMutableArray *)objectsOwnedByMe {
if (_objectsOwnedByMe == nil) {
_objectsOwnedByMe = [[NSMutableArray alloc] init];
}
return _objectsOwnedByMe;
}
This would explain both problems: Since it is nil it never retains the objects and therefore they go away when removed from the other array, and also why they are never added to the _objectsOwnedByMe array.

Where do I create global variables for an iOS app?

Here is my code:
I want to be able to create a global NSMutableArray that can store Budget* objects that can then be written to a .pList file... I'm only learning what pLists are, and I am a bit hazy about how to implement them...
Where am I going wrong here?
- (IBAction)btnCreateBudget:(id)sender
{
Budget *budget = [[Budget alloc] init];
budget.name = self.txtFldBudgetName.text;
budget.amount = [self.txtFldBudgetAmount.text intValue];
// Write the data to the pList
NSMutableArray *anArray = [[NSMutableArray alloc] init]; // I want this to be a global variable for the entire app. Where do I put this?
[anArray addObject:budget];
[anArray writeToFile:[self dataFilePath] atomically:YES];
/* As you can see, below is where I test the code. Unfortunately,
every time I run this, I get only 1 element in the array. I'm assuming
that this is because everytime the button is pressed, I create a brand new
NSMutableArray *anArray. I want that to be global for the entire app. */
int i = 0;
for (Budget * b in anArray)
{
i++;
}
NSLog(#"There are %d items in anArray",i);
}
-(NSString *) dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"BudgetData.plist"];
}
edit: I'd like to add that I am creating the anArray array so that it can be accessible by other views. I understand that this can be done with NSNotification? or Should I do this the appDelegate classes? The end goal is to have the anArray object populate a UITableView that is in a separate View.
Just put the declaration outside the method instead of inside it.
NSMutableArray *anArray = nil;
- (IBAction)btnCreateBudget:(id)sender
{
...
if ( anArray == nil )
anArray = [[NSMutableArray alloc] init];
...
}
If it's only used inside the one file, make it "static" instead to prevent name collisions with other files:
static NSMutableArray *anArray = nil;
If it's only used inside the one method, make it "static" and put it inside that method:
- (IBAction)btnCreateBudget:(id)sender
{
static NSMutableArray *anArray = nil;
...
if ( anArray == nil )
anArray = [[NSMutableArray alloc] init];
...
}
Note that people usually use some kind of naming convention for global variables, like "gArray", to easily differentiate them from local variables, instance variables, or method parameters.
Global variable is not necessary in this case. You can do something like this:
Read old data to mutable array (initWithContentsOfFile:).
Add new record to the array.
Save the array to same file.
But the second problem in your code is that if your Budget class is not a property list type (NSString, NSData, NSArray, or NSDictionary objects) writeToFile: will not save it sucessfully.
You need to make sure that your Budget class invokes NSCoder and then the NSCoder initWithCoder: and NSCoder decodeWithCoder: methods. Otherwise, writeToFile: will not work for you NSObject class.
But I digress. The answer to the original question should be the following.
In your .h file you need to do the following.
#interface WhateverClassName : UIViewController
{
NSMutableArray *anArray;
}
#property(nonatomic, retain) NSMutableArray *anArray;
#end
Then, you need to make sure you #synthesize the NSMutableArray so that you don't get any freaky warnings. This is done just after the #implementation line in your .m file.
Then, within the function that you want it to be allocated into memory, simply do the following.
anArray = [[NSMutableArray alloc] initWithObjects:nil];
This is now a global variable. It is global in the sense that it can be used from any function and is not limited to use in one function.
If you would like to have data accessible to the entire application or context ("global"), you can use a singleton. However, do this with care and make sure it is actually necessary and appropriate. I would suggest doing plenty of reading up on it prior to any implementation of a singleton. Carter Allen has a good basic implementation here.
According to "The end goal is to have the anArray object populate a UITableView that is in a separate View" you wouldn't need to write anything to a file, database or singleton. Just set the object. Like stated by Sebastien Peek.
If you wish for offline data storage, look into sqlite, json, plist , etc

Setting values for an array of UILabels which in turn is in NSMutableDictionary

I have an array of UILabels as follows:
#interface ClueLabels : NSObject
#property (nonatomic) IBOutlet UILabel *label1, *label2, *label3, *label4, *label5, *label6, *label7, *label8;
#property (nonatomic, readonly) NSArray *labels;
+ (ClueLabels *)clueLabels;
- (void)labelsDidLoad;
#end
Which is implemented as a singleton:
#implementation ClueLabels
#synthesize label1 = _label1;
#synthesize label2 = _label2;
#synthesize label3 = _label3;
#synthesize label4 = _label4;
#synthesize label5 = _label5;
#synthesize label6 = _label6;
#synthesize label7 = _label7;
#synthesize label8 = _label8;
#synthesize labels = _labels;
+ (ClueLabels *)clueLabels {
static ClueLabels *singleton;
static dispatch_once_t once;
dispatch_once(&once, ^{
singleton = [[ClueLabels alloc] init];
});
return singleton;
}
- (void)labelsDidLoad {
_labels = [[NSArray alloc] initWithObjects:_label1, _label2, _label3, _label4, _label5, _label6, _label7, _label8, nil];
NSLog(#"Clue labels did load");
}
#end
Then stored in an NSMutableDictionary as follows:
NSMutableArray *keyArray;
NSMutableArray *valueArray;
keyArray = [NSMutableArray arrayWithObjects:#"answerButtons", #"clueLabels", nil];
valueArray = [NSMutableArray arrayWithObjects: [AnswerButtons answerButtons], [ClueLabels clueLabels], nil];
NSMutableDictionary *externals = [NSMutableDictionary dictionaryWithObjects:valueArray
forKeys:keyArray];
Now I want to write to the labels (for instance, to clear them), but they are buried in a singleton and stored as an array inside key-valued dictionary. Conceptually, I need to send a message to one object in the dictionary using key-value syntax, and then loop over the objects in that array, clearing them with setText#"" for instance. Is this a nested message with a loop? I can't quite figure out how to structure this task. Thank you.
EDIT: I got to thinking and decided (maybe incorrectly) that because the labels are in a singleton, I don't need to access them through the dictionary at all, I just need to access the singleton and the dictionary will be updated automatically. So I decided my question/problem is more "how to talk to my singleton". I looked around SO some and then figured out I could do something like this:
NSLog(#"%#", [ClueLabels clueLabels].labels)
Which returns the details of each label. I'd like to be able to get the count of labels so I can loop over them and set their titles to arbitrary strings. Can I build around [ClueLabels clueLabels].labels or do I need a different approach? Thanks.
U can either loop on the UILabels as u said. Or u can use "makeObjectsPerformSelector" method on the array, which will (as its name says) make all the objects in it perform the selector u sent. Thus saves the looping part. But either way u need to get a hold of the array of labels contained in the dictionary
Mission accomplished! Here's how I did it:
// Clear the fields
for (NSInteger idx = 0; idx < 8; idx++) {
UILabel *l = [[ClueLabels clueLabels].labels objectAtIndex:idx];
[l setText:#""];
Obviously, I'm still learning!
You could also do something like this:
for (UILabel *theLabel in clueLabels)
{
[theLabel setText:#"];
}