Objective-C - how to put reference ID while using NSMutableArray? - ios

Is there anyway on [randomSelection addObject:renderView]; assign custom peerID references (such as id: cgiLoC0OdMNtVph3aMZV or cxeeeLoC0OdMNtVph3aMZV), for later to query from some random/unsorted UIView's?
For example: while create i have such as below:
//#property (copy, nonatomic) NSMutableArray *randomSelection;
//UIView *renderView;
//renderView = [[UIView alloc] initWithFrame:CGRectMake(padding_x, padding_y, 50, 50)];
NSMutableArray *randomSelection = [[NSMutableArray alloc] init];
[randomSelection addObject:renderView];
//self.randomSelection = randomSelection;
But there are some situation when i need to remove some of the UIView but in that case i only have some peerID references. How do i have some custom reference while using NSMutableArray
- (void)callingRemoveMethod:(NSString *)peerID {
NSLog(#"removed %#", peerID);
/* instead of objectAtIndex is there a way to query
it by any custom references? */
//UIView *test = [self.randomSelection objectAtIndex:0];
//test.backgroundColor = [UIColor redColor];
}

You could use an NSMutableDictionary instead and use your ID as the key to the dictionary.
UIView has a tag property (an int) that you can use for this purpose, but it can't be arbitrary information (just an int)
EDIT:
NSMutableDictionary *jobs = [NSMutableDictionary
dictionaryWithDictionary:#{
#"Audi TT" : #"John",
#"Audi Quattro (Black)" : #"Mary",
#"Audi Quattro (Silver)" : #"Bill",
#"Audi A7" : #"Bill"
}];
// Transfer an existing job to Mary
[jobs setObject:#"Mary" forKey:#"Audi TT"];
// Finish a job
[jobs removeObjectForKey:#"Audi A7"];
// Add a new job
jobs[#"Audi R8 GT"] = #"Jack";

Related

Objective-C: Selectively combine two arrays

I am aware of how normal NSArray concatenation works in Objective-C. This is not that question.
I have data that is being incrementally updated from a web service. My object has the following class definition (with a lot removed):
// NoteTemplate.h
#import <UIKit/UIKit.h>
#interface NoteTemplate
#property (copy, nonatomic) NSString *objectId;
I am caching a list of these on-device and checking at launch to see if there are any new or updated NoteTemplate objects in my database to load. So, I end up with two arrays:
NSArray <NoteTemplate *> *oldArray
NSArray <NoteTemplate *> *newArray
If there are no updates, then all I need to do is simply concatenate the two arrays together and that's that.
If there are updates, however, I want to combine the two arrays, but whenever there is a common objectId, the item in newArray should take precedence over the item in oldArray.
Thus far, I am brute-forcing it like this:
- (void)updateNoteTemplatesWithArray:(NSArray *)newTemplates {
NSArray *oldTemplates = [self getNoteTemplates];
NSMutableArray *combined = [NSMutableArray arrayWithArray:newTemplates];
for (NoteTemplate *noteTemplate in oldTemplates) {
NSArray *matches = [combined filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id blockTemplate, NSDictionary<NSString *,id> *bindings) {
return [((NoteTemplate *)blockTemplate).objectId isEqualToString:noteTemplate.objectId];
}]];
if (matches.count == 0) {
[combined addObject:noteTemplate];
}
}
[self setNoteTemplates:[combined copy]];
}
Is there a more optimized way to do this? I can't see that this will affect performance at all, so perhaps an optimization is unnecessary. Still, this approach feels hacky and way over-engineered.
To extend #Larme's suggestion with Set usage you can try the following approach:
#interface NoteTemplate: NSObject
#property (copy, nonatomic) NSString *objectId;
#property (copy, nonatomic) NSString *text;
- (instancetype)initWithObjectId:(NSString *)objectId text:(NSString *)text;
#end
#implementation NoteTemplate
- (instancetype)initWithObjectId:(NSString *)objectId text:(NSString *)text {
self = [super init];
if (self != nil) {
_objectId = objectId;
_text = text;
}
return self;
}
- (BOOL)isEqual:(id)object {
return [self.objectId isEqualToString:[object objectId]];
}
#end
And the usage code:
NoteTemplate *nt1 = [[NoteTemplate alloc] initWithObjectId:#"1" text:#"old set"];
NoteTemplate *nt2 = [[NoteTemplate alloc] initWithObjectId:#"2" text:#"old set"];
NoteTemplate *nt3 = [[NoteTemplate alloc] initWithObjectId:#"1" text:#"new set"];
NoteTemplate *nt4 = [[NoteTemplate alloc] initWithObjectId:#"3" text:#"new set"];
NSSet <NoteTemplate *> *oldSet = [NSSet setWithObjects:nt1, nt2, nil];
NSSet <NoteTemplate *> *newSet = [NSSet setWithObjects:nt3, nt4, nil];
NSMutableSet <NoteTemplate *> *mergedSet = [newSet mutableCopy];
[mergedSet unionSet:oldSet];
for (NoteTemplate *note in mergedSet) {
NSLog(#"Set item %# %#", note.objectId, note.text);
}
After executing this code you'll see in the log:
Set item 3 new set
Set item 1 new set
Set item 2 old set
I assume that's what you were looking for.
I don't know if I'd call this elegant but it's a less brutish approach. Instead of filtering combined at every pass through the loop, get all the new IDs in advance and check the ID list in the loop.
NSMutableArray <NoteTemplate *> *combined = [NSMutableArray arrayWithArray:newTemplates];
NSArray <NSString *> *newTemplateIds = [newTemplates valueForKey:#"objectId"];
for (NoteTemplate *oldTemplate in oldTemplates) {
if (![newTemplateIds containsObject:oldTemplate.objectId]) {
[combined addObject:oldTemplate];
}
}

Adding objects to instance variable array

Can someone help me out on this:
Im creating a property in my TableVC.m file :
#property NSMutableArray *savingBeaconSpecs;
In my Viewdidload I instantiate the array:
NSMutableArray *savingBeaconSpecs = [[NSMutableArray alloc]init];
Now I do requests to the server, and I want to save the returned JSON into objects and save these each time in the array. So I did the following in the ConnectionDidFinishLaunching:
self.artworkArray = [NSJSONSerialization JSONObjectWithData:self.data options:0 error:&err];
NSLog(#"Log ArtworkArray in ConnectionDidFinishLoading%#", self.artworkArray);
And:
Artwork *artwork = [[Artwork alloc]init];
artwork.title = [self.artworkArray valueForKey:#"name"];
artwork.artist = [[self.artworkArray objectForKey:#"artist"] valueForKey:#"name"];
artwork.CreationYear = [self.artworkArray valueForKey:#"creationYear"];
artwork.categorie = [[self.artworkArray objectForKey:#"exposition"] valueForKey:#"name"];
Now I want to save this object into the savingBeaconSpecs NSMutableArray
[self.savingBeaconSpecs addObject:artwork];
But the NSMUtableArray savingBeaconSpecs always returns 0 when i try log his content
Anyone please?
Because you declare it locally in your viewDidLoad :
NSMutableArray *savingBeaconSpecs = [[NSMutableArray alloc]init];
you should use
self.savingBeaconSpecs = [[NSMutableArray alloc]init];
and
[self.savingBeaconSpecs addObject:artwork];
and declare your property as (without the first capital S)
#property NSMutableArray *savingBeaconSpecs;
To instantiate the array, you should do:
self.savingBeaconSpecs = [[NSMutableArray alloc] init];
or equally good:
self.savingBeaconSpecs = [NSMutableArray array];

Why NSMutableDictionary is losing data?

I am using NSMutableDictionary. Declare it using property as follows:
#property (strong, nonatomic) NSMutableDictionary *books;
In the method, I am assigning it values.
- (void)setUpBooks {
if (self.books == nil){
self.books = [[NSMutableDictionary alloc] init];
}
// my code goes here...
[self.books setObject:book forKey:key];
NSLog(#"books : %#",self.books);
NSLog(#"books Count : %d",[self.books count]);
}
In this NSLog it's showing correct values. But when I tried to use self.books in another method its showing null. I don't know where my data is losing.
Easy way to use NSMutableDictionay.
Try this,
NSMutableDictionary * books = [[NSMutableDictionary alloc] init];
NSDictionary *book = [[NSDictionary alloc] initWithObjectsAndKeys:#"Value",#"BOOK_KEY", nil];
[books addEntriesFromDictionary:book];
Hope it's help you.

How to store data in class in iOS programming?

The goal, to create a class which contains an array of data to be used throughout the application by other classes.
I have this GlobalObject.h
It declares the array to be used to store the data.
#import <Foundation/Foundation.h>
#interface GlobalObjects : NSObject
#property (retain) NSMutableArray *animals;
-(id)init;
#end
I have this GlobalObject.m.
It contains the NSDictionary data and stores in to the array.
#import <Foundation/Foundation.h>
#interface GlobalObjects : NSObject
#property (retain) NSMutableArray *animals;
-(id)init;
#end
#import "GlobalObjects.h"
#implementation GlobalObjects
#synthesize animals;
-(id)init{
self = [super init];
if (self) {
// Define the data
NSArray *imagesValue = [[[NSArray alloc] initWithObjects:#"dog.wav",#"cat.png",#"bird.png",nil] autorelease];
NSArray *audioValue =[[[NSArray alloc] initWithObjects:#"dog.wav",#"cat.wav",#"bird.wav",nil] autorelease];
NSArray *descriptionValue = [[[NSArray alloc] initWithObjects:#"Dog",#"Cat",#"Bird",nil] autorelease];
// Store to array
for (int i=0; i<8; i++) {
NSDictionary *tempArr = [NSDictionary dictionaryWithObjectsAndKeys:[imagesValue objectAtIndex:i],#"image", [audioValue objectAtIndex:i],#"audio", [descriptionValue objectAtIndex:i], #"description", nil];
[self.animals addObject:tempArr];
}
}
return self;
}
#end
Here's how I call it.
// someOtherClass.h
#import "GlobalObjects.h"
#property (nonatomic, retain) GlobalObjects *animalsData;
// someOtherClass.m
#synthesize animalsData;
self.animalsData = [[[GlobalObjects alloc] init] autorelease];
NSLog(#"Global Object %# ",self.animalsData.animals);
Now the problem is, when I call this array in another class, it always returns null.
I'm new to iOS programming. So probably my method is wrong?
You forgot to allocate the animals array in the init method of "GlobalObjects":
self.animals = [[NSMutableArray alloc] init];
If you don't do this, self.animals is nil and addObject has no effect.
Since you do not use ARC, remember to release the array in dealloc.
EDIT: As #H2CO3 and #Bastian have noticed, I forgot my pre-ARC lessons. So the correct way to allocate self.animals in your init method is
self.animals = [[[NSMutableArray alloc] init] autorelease];
and in dealloc you have to add
self.animals = nil;
before calling [super dealloc]. I hope that I got it right now!
Yes, it's wrong - an instance variable isn't tied to a class itself, but to a particular instance of the class. The Cocoa-standard solution to this problem is creating a shared instance of the class - instead of
elf.animalsData = [[[GlobalObjects alloc] init] autorelease];
write
elf.animalsData = [GlobalObjects sharedInstance];
and implement the + sharedInstance method like this:
+ (id)sharedInstance
{
static shared = nil;
if (shared == nil)
shared = [[self alloc] init];
return shared;
}
As #MartinR pointed out, you make another mistake: you don't create the array you're adding objects to - then it remains nil, cancelling out the effect of all method calls on itself. You have to alloc-init a mutable array for it in the - init method.

iOS Memory Management/Persistence Issue

I am using a helper class in my app, to access a database and return an array of 5 objects, which I then assign to a property in my view controller.
In my view controller I call it like so:
- (void)viewDidLoad
{
[super viewDidLoad];
DatabaseHelper *helper = [[DatabaseHelper alloc] init];
self.trailersArray = [helper retrieveTrailers];
// Set trailer for first round
self.trailer = [self.trailersArray objectAtIndex:0];
// Prepare audio player
[self prepareToPlay];
// Swoop film screen in
[self swoopFilmScreenInAndAddPlayButton];
// Fade title in
[self fadeInTitleScreen];
// Initialise button array and set buttons to hidden
self.buttonOutlets = [NSArray arrayWithObjects:self.button1, self.button2, self.button3, self.button4, nil];
[self hideButtons];
// Initialise rounds
self.round = [NSNumber numberWithInt:-1];
// Initialise score which will also update graphics
[self initialiseScore:self.round];
self.scoreInteger = 0;
// Start first round
self.round = [NSNumber numberWithInt:0];
NSLog([self.trailersArray description]);
// User will trigger playing with Play IBAction
// User will trigger stop playing with Stop Playing IBaction
}
My problem is that once viewDidLoad is finished, the helper object seemingly disappears, as do its objects, and my self.trailersArray ends up pointing at nothing.
How do I fix this? Have tried deep copying, and using a retain attribute on the property but not working.
I can't use a class method because it ruins my helper object database methods but I am intrigued as to how class methods get around this memory problem?
Thanks for any help.
Alan
EDIT: As requested, code for retrieveTrailers below:
-(NSArray *)retrieveTrailers
{
// Get list of mp3 files in bundle and put in array
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *dirContents = [fm contentsOfDirectoryAtPath:bundleRoot error:nil];
NSPredicate *fltr = [NSPredicate predicateWithFormat:#"self ENDSWITH '.mp3'"];
NSArray *onlyMP3s = [dirContents filteredArrayUsingPredicate:fltr];
NSMutableArray *arrayOfTrailersWithMP3s = [[NSMutableArray alloc] init];
// Query database for objects where (unique id) = (mp3 file title)
for(NSString *string in onlyMP3s)
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Trailer"];
NSString *stringWithNoFileExtension = [string stringByReplacingOccurrencesOfString:#".mp3" withString:#""];
request.predicate = [NSPredicate predicateWithFormat:#"unique = %#", stringWithNoFileExtension];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *array = [[self managedObjectContext] executeFetchRequest:request error:nil];
Trailer *trailer = [array lastObject];
NSLog([NSString stringWithFormat:#"Trailer Available:%#", trailer.title]);
if(trailer)
{
[arrayOfTrailersWithMP3s addObject:trailer];
}
}
// Choose five of the trailers at random and return
NSMutableArray *fiveRandomSelectedTrailers = [[NSMutableArray alloc] init];
int numberOfAvailableTrailers = [arrayOfTrailersWithMP3s count];
for(int i=0;i<5;i++)
{
int rand = (arc4random() % (numberOfAvailableTrailers));
Trailer *trailer = [arrayOfTrailersWithMP3s objectAtIndex:rand];
NSLog([NSString stringWithFormat:#"Trailer Chosen:%#", trailer.title]);
[fiveRandomSelectedTrailers addObject:trailer];
[arrayOfTrailersWithMP3s removeObject:trailer];
numberOfAvailableTrailers --;
}
return fiveRandomSelectedTrailers;
}
If that really is you viewDidLoad code what you are doing is creating a local object of your helper class which is then out of scope once the function has completed.
If you have a retained property of the helper class, you don't need the declaration: try doing it this way
In your .h file have a line like this:
#property(retain, nonatomic) DatabaseHelper *helper;
In your .m file make sure you have:
#synthesize helper;
In your viewDidLoad:
self.helper = [[DatabaseHelper alloc] init];
self.trailersArray = [self.helper retrieveTrailers];
This way you are creating an object of your helper class and assigning it to property, instead of creating a local variable. And, as you can see, you can use the property object of your helper class when you want to send it messages.
I'm assuming you are using MRC instead of ARC.

Resources