Why my own Object not populate NSMutableArray property? - objective-c

Hi i want to implement my own Objects to manage my data, i was trying to make a two classes.
Class Continents that contains a Continent Objects
Here is my implementation:
#implementation OsContinents
#synthesize continentes;
-(id)init{
return [super init];
}
-(NSUInteger)count{
NSLog(#"%u",[continentes count]);
return [continentes count];
}
-(void)add:(OsContinent *)continente{
[continentes addObject:continente];
}
-(OsContinent *)getElementByIndex:(NSUInteger)index{
return [continentes objectAtIndex:index];
}
-(void)deleteContinentByIndex:(NSUInteger)index{
return [continentes removeObjectAtIndex:index];
}
-(void)deleteContinent:(OsContinent *)objContinent{
return [continentes removeObject:objContinent];
}
-(NSMutableArray *)getAll{
return continentes;
}
#end
Next i want to populate *continents Property with "Continent" Objects like this.
OsContinents *continentesCollection = [[OsContinents alloc] init];
for (NSString *strContinente in [data allKeys]) {
OsContinent *con = [[OsContinent alloc] init];
[con setContinente:strContinente];
NSLog(#"%#",[con getContinente]);
[continentesCollection add:con];
}
NSLog(#"%u",[continentesCollection count]);
But allways got ZERO in de count Method.
Note: NSLog(#"%#",[con getContinente]) print de data OK, the Continent Object is OK, the problem is in the "*continentes" inside the Continents Object-
Any Clue?

Your initializer does nothing but initialize the superclass. Use it to set up your own class:
- (id)init
{
self = [super init];
if (self)
{
_continentes = [[NSMutableArray alloc] init];
}
return self;
}
Otherwise, continentes will remain nil. Messaging nil is valid: methods simply don't do anything, and return 0.
If you want to completely hide the underlying mutable array (which is perfectly fine) then don't advertise it in your .h file as a property. Instead, at the beginning of your #implementation, declare a semi-private instance variable:
#implementation OsContinents
{
NSMutableArray *_continentes;
}
I say "semi-private" because you can always use the runtime engine to introspect objects. But it'll be hidden from normal use. If you ever subclass your object, you can always move the instance variable declaration from your #implementation to your #interface so that subclasses can get at it.

Related

NSObject can not access property and method

I have a very strange problem, I have two classes the first one is a sub class of NSObject class it contains a method that add an object to its array. See the code below:
#import "portfolio.h"
#implementation portfolio
-(void) addStockObject:(stockHolding *)stock
{
[self.stocks addObject:stock ];
}
+(portfolio *) alloc
{
return [self.superclass alloc];
}
-(portfolio *) init
{
self.stocks=[[NSMutableArray alloc]init];
return self;
}
-(NSString *)getCurrentValue
{
stockHolding *stockInArray;
float currentValue=0.0;
for (NSInteger *i=0; i<[self.stocks count]; i++) {
stockInArray = [self.stocks objectAtIndex:i];
currentValue+=stockInArray.currentValue;
}
return [NSString stringWithFormat:#"Current Value: %f",currentValue];
}
#end
so when i call the method -(void) addStockObject:(stockHolding *)stock, i get the following error(during runtime):
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[NSObject addStockObject:]: unrecognized selector
sent to instance 0x8b48d90'
The calling code is:
p=[[portfolio alloc]init];
[p addStockObject:s];
portfolio *p;
anyone can tell me what is wrong?
the other class has a property and it seems that it can not access it during compile time.
I'm really confused.
Thank you,
missa
First, never override +(portfolio *) alloc.
Second, init methods must call another init method and you must always check self for nil before setting ivars. Apple recommends against using properties to set ivars in init methods and init methods should always return instancetype in compilers that support it or id in those that don't.
-(instancetype) init
{
self = [super init];
if (self)
{
_stocks = [[NSMutableArray alloc] init];
}
return self;
}

NSArray error: initializer element is not constant

I declared this array in my view controller implementation file:
NSMutableArray *images = [NSMutableArray array];
I wanted an empty, mutable array that I would later add UIImageViews to. It always return the error:
initializer element is not constant
The proper solution is to make images an instance variable and then you initialize it in your init method.
#implementation SomeClass {
NSMutableArray *images; // instance variable
}
- (id)init {
self = [super init];
if (self) {
images = [[NSMutableArray alloc] init];
}
return self;
}
This is an example. If you have a specific init... method, use that instead.
As an instance variable, other methods in the class now have access to images and each instance of the class gets its own copy of images.
You need to show more code, but the problem is pretty obvious if that really is the line that is erroring out.
You can only dynamically initialization variables at the time of declaration in very specific spots. Dynamically includes calling a method.
NSMutableArray *a = [NSMutableArray array]; // this will error.
static NSMutableArray *a = [NSMutableArray array]; // this will error.
#implementation Booger
{
NSMutableArray *a = [NSMutableArray array]; // this will error
}
NSMutableArray *a = [NSMutableArray array]; // this will error.
- (void)bar
{
NSMutableArray *a = [NSMutableArray array]; // this is fine
}
Sounds like you need to dive a bit more deeply on the whole object-oriented thing. A class is a collection of functions called methods that either operate on the class (class methods) or a single instance of the class (instance methods). An instance can store state that is accessible to all methods when any method is invoked on that instance. In traditional OO, such state is stored in instance variables. Typically, you would define a pair instance methods that set and get that instance variable's value. These are called accessors or setter/getter. In modern Objective-C, we use properties to declare both the instance variables and the methods that access the instance variable.
Thus:
#interface MyClass:NSObject
#property(strong) NSMutableArray *myArray;
#end
#implementation MyClass
// the #property will automatically create an instance variable called _myArray,
// a getter method called -myArray and a setter called -setMyArray:
- init
{
self = [super init];
if (self) {
_myArray = [NSMutableArray array]; // set the ivar directly in init
}
return self;
}
- (void)maybeAddThisThing:(Thing *)aThing
{
if ([aThing isCool] && ![[self myArray] containsObject:aThing]) {
[[self myArray] addObject:aThing];
}
}
- (void)nukeFromOrbit
{
[self setMyArray:[NSMutableArray array]];
// or you could do [[self myArray] removeAllObjects];
}
The return of your NSMutableArray construction does not have an address known at compile time. You can only initialise dynamically inside method scope.
Static initialisation would be fine though: For example, NSString *myString = #"Hello String"; in global scope will compile just fine.

Determine class in init method

I have a global dictionary with Class objects and NSString keys. Every custom subclass of my Property class can register itself on the Property superclass using
+ (void)registerPropertyClass:(Class)pclass forNamePrefix:(NSString *)namePrefix
A Property is initialized with a name and value. What I want to do now is return a different class from the init method based on the name prefix (if a registered class matches).
Would something like this be correct?
- (id)initWithName:(NSString *)name value:(NSString *)value
id instance = self;
NSArray *registeredPrefixes = [kCKPropertyClasses allKeys];
for (NSString *prefix in registeredPrefixes) {
if ([name rangeOfString:prefix].location == 0) {
instance = [[kCKPropertyClasses objectForKey:prefix] alloc];
break;
}
}
self = [instance init];
if (self) {
self.name = name;
self.value = value;
}
return self;
}
UPDATE: Forgot to mention that this project is using ARC (so no retain/release)
Without compiling it, that'll work, but leak under manual-retain-release. You need to release self prior to the reassignment self = [instance init];.
With ARC, it should be fine.

Objective-C :: using a method to change an object

I have a class called "CardSet", containing an NSMutableArray* cardSet to hold "cards", which I extend to make "DeckCards". I'd like "CardSet" to have a method called "(void)addCard:(Card*)" (and similarly a method "removeCard"). I'd like "addCard" to some how have access to and set cardSet. Even better I'd like to use the "addCard" method to initialise cardSet. The class file "CardSet.h" reads:
#import < Cocoa/Cocoa.h >
#import < Card.h >
#interface CardSet : NSObject {
NSMutableArray* cardSet;
}
-(id)init;
-(NSMutableArray*)getCardSet;
-(void)setCardSet:(NSMutableArray *)new_cardset;
-(Card*)getCard:(NSInteger) index;
**-(void)addCard:(Card*) new_card;**
-(void)removeCard:(Card*) old_card;
-(void)dealloc;
#property (readwrite, retain, getter=getCardSet, setter=setCardSet) NSMutableArray* cardSet;
#end
and the method file reads:
#import "CardSet.h"
#implementation CardSet
-(id)init{
if( self = [super init] ){} //will add initialisations here later
return self;
}
-(NSMutableArray*)getCardSet{
return cardSet;
}
-(void)setCardSet:(NSMutableArray *)new_cardSet{
cardSet = new_cardSet;
}
-(Card*)getCard:(NSInteger)index{
return [cardSet objectAtIndex:index];
}
**-(void)addCard:(Card *)new_card{
[cardSet addObject:new_card];
}**
-(void)removeCard:(Card *)old_card{
[cardSet removeObject:old_card];
}
-(void)dealloc{
[cardSet release];
[super dealloc];
}
#synthesize cardSet;
#end
This compiles just fine. I'd like to initialise a "DeckCards" instance using its "addCard" method 52 times. When I call addCard 52 times in a DeckCards setter method, and ask for the size of its "cardSet", I'm returned 0.
This appears to be a scope or privileges problem? Can the "addCard" method have any setter privileges? Must a setter argument be the same as the return and respective member type?
[I can work around the above by creating an NSMutableArray object "deck_cards_temp" outside of "DeckCard", add 52 cards to this, and pass it to set the member of my "DeckCards" instance via the setter inherited from "CardSet". This is not very satisfactory!]
What do you advise? Many thanks in advance for your help and patience.
You are never actually creating the cardSet object. You should be creating it in your -init method:
-(id)init
{
if( self = [super init] )
{
cardSet = [[NSMutableArray alloc] init];
}
return self;
}
Because you never actually create the array, all the calls to -addCard: are being sent to a nil object.
When you pass in an array to -setCardSet:, you are passing in an initialized array so the array is no longer nil and the -addCard: calls work fine.
CardSet.h
#import <Cocoa/Cocoa.h>
// For know we just need to know there is a class named "Card" being used but implemented later
#class Card;
#interface CardSet : NSObject {
NSMutableArray *cardSet;
}
// Here are the methods according to "correct" naming conventions
- (Card *)cardAtIndex:(NSInteger)index;
- (void)addCard:(Card *)card;
- (void)removeCard:(Card *)card;
// This will help us and forget about writing the setter/getter
#property (nonatomic, retain) NSMutableArray *cardSet;
#end
CardSet.m
#import "CardSet.h"
// Now we tell the compiler what "Card" is and what methods etc. it has
#import "Card.h"
#implementation CardSet
#synthesize cardSet;
- (id)init {
if (self = [super init]) {
// If we don't create the cardSet, how are we able to work with it!?
NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.cardSet = anArray;
[anArray release];
}
return self;
}
- (Card *)cardAtIndex:(NSInteger)index {
return [cardSet objectAtIndex:index];
}
- (void)addCard:(Card *)card {
[cardSet addObject:card];
}
- (void)removeCard:(Card *)card {
[cardSet removeObject:card];
}
- (void)dealloc {
[cardSet release];
[super dealloc];
}
#end
As Abizern already noted: Naming the array the same as your class is a bad thing.
I would shorten that init method:
- (id)init {
if (self = [super init]) {
// If we don't create the cardSet, how are we able to work with it!?
self.cardSet = [NSMutableArray array];
}
return self;
}

Accessing collection through KVC (to protect collection and be KVO compliant)

I have a class Test which has an array of Foos. I want to provide access to the Foos without exposing the ivar directly. I'm trying to make this KVC compliant (also to pave the way for KVO compliance). I have:
Test.h
#interface Test : NSObject
{
NSMutableArray *foos;
}
#property (readonly, copy) NSMutableArray *foos;
#end
Test.m
- (id) init
{
self = [super init];
if (self != nil)
{
foos = [[NSMutableArray array] retain];
}
return self;
}
- (NSMutableArray*) foos
{
return [self mutableArrayValueForKey:#"foos"];
}
- (NSUInteger)countOfFoos
{
return [foos count];
}
- (id)objectInFoosAtIndex:(NSUInteger)index
{
return [foos objectAtIndex:index];
}
- (NSArray *)foosAtIndexes:(NSIndexSet *)indexes
{
return [foos objectsAtIndexes:indexes];
}
- (void)insertObject:(id)key inFoosAtIndex:(NSUInteger)index
{
[foos insertObject:key atIndex:index];
}
- (void)insertFoos:(NSArray *)foosArray atIndexes:(NSIndexSet *)indexes
{
[foos insertObjects:foosArray atIndexes:indexes];
}
- (void)removeObjectFromFoosAtIndex:(NSUInteger)index
{
[foos removeObjectAtIndex:index];
}
- (void)removeFoosAtIndexes:(NSIndexSet *)indexes
{
[foos removeObjectsAtIndexes:indexes];
}
This enters an infinite loop when a client tries to add a Foo:
Test *test = [[Test alloc] init];
NSMutableArray *foos = test.foos;
[foos addObject:#"adding object"]; // infinite loop here
What am I doing wrong?
- (NSMutableArray*) foos
{
return [self mutableArrayValueForKey:#"foos"];
}
An accessor should not use KVC to get the value of the property being accessed; the idea is that KVC goes through the accessors, because the accessors are closer to the value than KVC is.
The correct implementation of foos should return a copy, mutable or otherwise, of the array. Here's how I'd do it:
- (NSArray *) foos
{
return [[foos copy] autorelease];
}
I would also make all of the accessors public. Anything that wants to mutate the array or randomly access elements at specific indexes can do so that way. It's still safe and encapsulated because they're going through your accessors, not directly accessing the array.
There's not really any reason to use the KVC protocol methods yourself unless you don't know what key you'll access at the time you write the code. For example, if you were writing the nib loader or the Cocoa Bindings system, you would use KVC.
The problem is that the proxy NSMutableArray returned by mutableArrayValueForKey: first has to get the real array, which it does through the "foos" method. Since that's the one that returns a proxy NSMutableArray it enters an infinite loop. One solution is to use another name:
- (NSMutableArray*) mutableFoos
{
return [self mutableArrayValueForKey:#"foos"];
}
I spent a very long time on this problem and wanted to get this through an accessor. I wanted to clarify in the answer for those coming in. This is what I did:
#property (nonatomic,readonly,getter=getTheFoos) NSMutableArray* foos;
Then obviously implemented:
- (NSMutableArray*)getTheFoos {
return [self mutableArrayValueForKey:#"foos"];
}
Had to be careful though, getFoos appears to be an (undocumented) KVC accessor, because this sends it into the same loop.
Then onto KVO:
Test* test= [[Test alloc] init];
NSObject* obj= [[NSObject alloc] init];
NSMutableArray* arrTheData= test.foos;
[test.foos insertObject:obj atIndex:0];
[arrFoos insertObject:obj atIndex:0];
arrFoos can read the updated, mutated array (it will have two objects in it), but inserting into it will not fire KVO. Somewhere on my adventures, I saw that the return from mutableArrayValueForKey: doesn't return an NSMutableArray*, but a subclass of it, which might be the cause of it.