multiple objects for a single index in an NSArray - objective-c

Is it possible to store multiple objects for a single index in an NSArray?

Sure there are many ways to do this with the most common being to assign a dictionary to each array element

Yes, you probably just want to have an NSArray of NSMutableArrays. You can then call something like this:
[[array objectAtIndex:2] addObject:obj];
or
[[array objectAtIndex:2] objectAtIndex:1];

I'm not sure how dynamic you want the multiple objects to be. How about creating a very simple class with properties of the multiple objects?
I was thinking about a struct but I don't think NSArrays like pointer objects.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#interface myCompound : NSObject
{
}
#property (nonatomic,strong) NSColor* colour;
#property (nonatomic,strong) NSRegularExpression* expression;
#end
#implementation myCompound
#synthesize colour;
#synthesize expression;
#end
And use this as the element type for NSArray
NSArray<myCompound*>* myArray;

Related

How do you set the text of a label to an array value?

I would like to set a value of my array to a label.
Array declaration:
//
// ViewController.h
// Cornell Notes
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
NSString *details[8][8];
NSString *subtitles[8];
}
I am not allowed to do:
self.label.text = subtitles[0];
How can I do this?
This:
#interface ViewController : UIViewController{
NSString *details[8][8];
NSString *subtitles[8];
}
Should Be:
#interface ViewController : UIViewController
#property (strong, nonatomic) NSMutableArray *details;
#property (strong, nonatomic) NSMutableArray *subtitles;
You could declare it similar to the way you had it, but I believe this is the preferred current syntax. Others may correct me on this. Most important is that we declare NSMutableArray's as opposed to NSString's. You are doing C-style declarations, which will be a bit different with these. I chose NSMutableArray as opposed to NSArray because it looks like you want to be able to add objects at runtime.
And this:
self.label.text = subtitles[0];
should be:
if (!_subtitles) _subtitles = [NSMutableArray new];
[_subtitles insertObject:self.label.text atIndex:0];
This line:
if (!_subtitles) _subtitles = [NSMutableArray new];
Is just to make sure that our _subtitles dictionary exists, and if it does, we make sure that it isn't overwritten. the [NSMutableArray new] syntax is something that I personally like because it looks clean; however, many prefer [[NSMutableArray alloc]init]. Just so you're aware.

Objective-C property, expose only superclass

I am declaring a property in my class in header file;
#property (readonly) NSArray *pages
That's how I want it to be exposed publicly. Internally though, I am going to allocating it as NSMutableArray so I can add/remove stuff from it. But to do that, I will have to type cast every time. Is there a better way to do this?
Thanks
Your approach is really bad. If you insist on exposing a mutable array with dynamic content, then modify your getter to return an immutable copy, otherwise you are going to get weird side effects and exceptions for mutations during fast enumeration.
There isn't a solution for this. You have to cast every time, or use different properties. Here is a sample for the second approach:
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSArray *pages;
-(void)addObject:(id)obj;
#end
#interface MyClass()
#property (nonatomic, strong, readwrite) NSMutableArray *mPages;
#end
#implementation MyClass
-(id) init {
self = [super init]
if (self){
_mPages = [NSMutableArray array];
}
return self;
}
-(NSArray*)pages {
return [NSArray arrayWithArray:self.mPages];
}
-(void)addObject:(id)obj {
[self.mPages addObject:obj];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
MyClass *m = [MyClass new];
[m addObject:#"x"]; // the collection is mutable
NSLog(#"%#",[m pages]); // but only accessible as an immutable copy
}
}
This will be expensive if you access the collection frequently, and may be out of sync with the internal mutable collection (which may be mutated while you iterate on the copy).
Copying can be avoided returning the internal mutable instance (NSMutableArray) disguised as an immutable class (NSArray), but that incurs the following risks:
The client could cast to mutable and change it.
The internal copy could be mutated. This will crash the application if you are iterating, or may cause an index out of range exception.
Note that the following idiom doesn't solve the problem:
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSArray *pages;
#end
#interface MyClass()
#property (nonatomic, strong, readwrite) NSMutableArray *pages;
#end
This lets you set the variable, but not use it as a different class than the one declared in the interface. In other words, it forces you to cast on every use:
[(NSMutableArray*)pages addObject:#"x"];

Removing and Adding same object to different classes

I'm reading a book to learn Objective-C. I'm stuck in one of the exercises. You have to make a program that has a class song, playlist, and music collection. If you create a song, it has to automatically add to the music collection, who has a NSMutableArray for collecting songs. And if You remove an object from music collection, the song has to remove from every playlist containing that song.
Song Interface
#import <Foundation/Foundation.h>
#interface Song : NSObject{
NSString *title;
NSString *artist;
NSString *album;
}
#property (copy) NSString *title;
#property (copy) NSString *artist;
#property (copy) NSString *album;
-(Song *) initWithNames:(NSString*) title1 and: (NSString*) artist1 and: (NSString*) album1;
#end
Playlist Interface
#import <Foundation/Foundation.h>
#interface Playlist : NSObject{
NSString * title;
NSMutableArray *collecsongs;
}
#property (strong) NSString *title;
#property (strong) NSMutableArray *collecsongs;
-(Playlist *) initWithName: (NSString *) name;
#end
Music Collection Interface
#import <Foundation/Foundation.h>
#import "Playlist.h"
#interface MusicCollection : NSObject{
NSMutableArray *collecplist;
Playlist *library;
}
#property (strong) NSMutableArray *collecplist;
#property (strong) Playlist *library;
#end
So if i create a song for example song1, is there a way if i add it to a playlist, automatically add it to the mastercoleection variable "library", instead of doing this
Song *song1 = [[Song alloc] initWithNames:#"Somebody That I Used To Know" and: #"Gotye" and: #"First Album"];
Playlist *Misrolas = [[Playlist alloc] initWithName: #"Misrolas"];
MusicCollection *music = [[MusicCollection alloc] init];
[Misrolas.collecsongs addObject: song1];//adds song1 to the playlist named "Misrolas"
[music.library.collecsongs addObject: song1];//adds song1 to the music collection
So i don't know what to do, i was thinking overriding maybe addObject:, but that doesn't seem right and easy, thanks for the help =)
I do it like this, is there more efficient or better ways to add it ???
-(void) addsong: (Song *)song addtocollection: (Playlist *) library{
NSAssert([song isKindOfClass: [Song class]], #"Not the same class");
[self.collecsongs addObject:song];
[library.collecsongs addObject: song];
}
-(Song *) initWithNames:(NSString*) title1 and: (NSString*) artist1 and: (NSString*) album1;
This is a pretty bad naming. Your selector shortens to initWithNames:and:and: (which is not really descriptive). Consider using
- (id)initWithTitle:(NSString *)title artist:(NSString *)artist album:(NSString *)album;
Notice how I use return type of (id) here. It allows easier subclassing, as any descendant class can use the init... "constructor" without any type mismatch warnings.
Speaking of your issue, I'd suggest you to expose only NSArray * property accessors (so that you cannot modify the array contents) and make a method on Playlist class:
- (void)addSong:(Song *)song
{
NSAssert([song isKindOfClass:[Song class]]);
[self.collecplist addObject:song];
}
That's OOP's encapsulation in action. You don't expose private interface (the array), you provide interface for adding exactly songs (you cannot add other kind of object), finally, you do a verification, that what you add is really a song. Here you can also add the song to your music collection.
You will probably find this easier if you call a method on the Playlist object to add the song, rather than accessing its collecsongs property. In that method, it can add the song to the array and then add it to the library. (And then you can make the collecsongs property return an NSArray, rather than an NSMutableArray, which seems much cleaner to me.

NSArray #property backed by a NSMutableArray

I've defined a class where I'd like a public property to appear as though it is backed by an NSArray. That is simple enough, but in my case the actual backing ivar is an NSMutableArray:
#interface Foo
{
NSMutableArray* array;
}
#property (nonatomic, retain) NSArray* array;
#end
In my implementation file (*.m) I #synthesize the property but I immediately run into warnings because using self.words is the same as trying to modifying an NSArray.
What is the correct way to do this?
Thanks!
I would declare a readonly NSArray in your header and override the getter for that array to return a copy of a private NSMutableArray declared in your implementation. Consider the following.
Foo.h
#interface Foo
#property (nonatomic, retain, readonly) NSArray *array;
#end
Foo.m
#interface Foo ()
#property (nonatomic, retain) NSMutableArray *mutableArray
#end
#pragma mark -
#implementation Foo
#synthesize mutableArray;
- (NSArray *)array
{
return [[self.mutableArray copy] autorelease];
}
#end
Basically, put the NSArray property in a category in your header file and the NSMutableArray property in the class extension in your implementation file. Like so...
Foo.h:
#interface Foo
#end
#interface Foo (Collections)
#property (nonatomic, readonly, strong) NSArray *someArray;
#end
Foo.m
#interface Foo ()
#property (nonatomic, readwrite, strong) NSMutableArray *someArray;
#end
Simple:
1) Don't use a property when it ain't one.
2) Code simplifies to:
- (NSArray *)currentArray {
return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray - otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it.
}
- (void)setArray:(NSArray *)array {
[mutableArray setArray:array];
}
When the object is alloced create the array, when it dies, dealloc the array.
When large effects happen at the mere use of a '.' operator, its easy to overlook hugely inefficient code. Accessors are just that. Also - if someone calls aFoo.array - the contract is to get access to foo's array members - but really its just a copy at the time of the call. The difference is real enough that it caused bugs in the other implentations posted here.
Update: this answer is not valid anymore. Use one of suggested solutions below.
These days you can do the following:
Foo.m:
#implementation Foo {
NSMutableArray* _array;
}
#end
Foo.h:
#interface Foo
#property (readonly, strong) NSArray* array;
#end
You can still address mutable _array by ivar from the inside of implementation and outside it will be accessible via immutable property. Unfortunately this doesn't guarantee that others can't cast it to NSMutableArray and modify. For better protection from idiots you must define accessor method and return immutable copy, however that might be very expensive in some cases.
I would actually agree with one of the comments above that it's better to use simple accessor methods if you need to return some read-only data, it's definitely less ambiguous.
That's because your property must match the actual ivar's class type.
A possible solution/workaround:
//Foo.h:
#interface Foo
{
NSMutableArray* mutableArray;
}
#property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
#end
//Foo.m:
#interface Foo ()
#property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
#end
#implementation
#synthesize mutableArray;
#dynamic array;
- (NSArray *)array {
return [NSArray arrayWithArray:self.mutableArray];
}
- (void)setArray:(NSArray *)array {
self.mutableArray = [NSMutableArray arrayWithArray:array];
}
#end
You're adding a private mutableArray property in a class extension and making the public array simply forward to your private mutable one.
With the most recent language extensions of ObjC I tend to remove the
{
NSMutableArray* mutableArray;
}
ivar block entirely, if possible.
And define the ivar thru the synthesization, as such:
#synthesize mutableArray = _mutableArray;
which will generate a NSMutableArray *_mutableArray; instance for you.
Simplest answer: your property type (NSArray) doesn't match your instance variable type (NSMutableArray).
This is yet another good reason that you shouldn't define your own backing variables. Let #synthesize set up your instance variables; don't do it by hand.

Objective-C NSArray

I'm new to Obj-C and iPhone SDK. The test application I'm stock with is a color switcher containing two buttons ("Back", "Forward") and one text label. The idea is to switch between rainbow colors (background) and setting an appropriate text label in a cyclic manner.
I declared NSArray (which is to contain colors names) in RainbowViewController.h, synthesized it in RainbowViewController.h and I can't add any string into that array.
This is "h" file:
#import <UIKit/UIKit.h>
#interface RainbowViewController : UIViewController {
IBOutlet UILabel *currentColorTextLabel;
NSArray *colorsArray;
NSString *msg;
}
#property (nonatomic, retain) IBOutlet UILabel *currentColorTextLabel;
#property (nonatomic, retain) NSArray *colorsArray;
#property (nonatomic, retain) NSString *msg;
- (IBAction) pressForwardButton;
- (IBAction) pressBackButton;
#end
This is "m" file:
#import "RainbowViewController.h"
#import <Foundation/Foundation.h>
#implementation RainbowViewController
#synthesize currentColorTextLabel;
#synthesize colorsArray;
#synthesize msg;
int currentArrayIndex = 0;
colorsArray = [[NSArray alloc] init]; //here i get "Initializer element is not constant" error message
[coloursArray addObject:#"Red"]; //here I get "Expected identifier or '(' before '[' token"
[coloursArray addObject:#"Orange"];
//etc
- (IBAction) pressForwardButton {
//here I'm going to increment currentArrayIndex, set an appropriate color, and update a currentColorTextLabel based on currentArrayIndex.
}
- (IBAction) pressBackButton {
}
//auto-genereted code here
#end
I'm new to obj-c as well, but I think you need to initialize the array with objects, or use an NSMutableArray if you want to add objects after it is created.
You have the code that should go in your init method just sitting out in the middle of the file. You can't set instance variables like that.
jasongetsdown is correct. You need to instantiate the NSArray object with the objects it will contain and nil terminated.
#"Red", #"Blue", nil
If you wish to have an array that you can change you need to make it a Mutable Array.
However, you have another problem here. Your property that you are synthesizing and allocating for is an object named colorsArray and you are trying to pass a method to a coloursArray object, two different spellings.