no visible #interface for NSMutable Array declares selector 'addStock' - objective-c

I cannot figure out why the addStock method is not working for my NSMutableArray object "giuPort" . I have properly connected all the class files. How do I make it so that the interface is visible / correct this error that comes up for each of the times I try using the addStock method?
The following snippet from my main.m file that is rendering the error is:
NSMutableArray *giuPort = [[NSMutableArray alloc]init];
[giuPort addStock:apple];
[giuPort addStock:lvs];
[giuPort addStock:verizon];
the class .h file in which I declare the NSMutableArray, etc:
#interface BNRPortfolio : NSObject
{
NSMutableArray *_stocks;
}
#property (nonatomic, copy) NSArray *stocks;
#property (nonatomic) float valueOfPort;
//instance methods
-(void)addStock:(BNRStockHolding *)s;
-(float)valueOfPort;
#end
the class .m file in which I implement the NSMutableArray, etc:
#implementation BNRPortfolio
// array get set stuff
-(void)setStocks:(NSArray *)s
{
_stocks = [s mutableCopy];
}
-(NSArray *)stocks
{
return [_stocks copy];
}
// instance methods
-(void)addStock:(BNRStockHolding *)s {
// check to see if array exists
if (!_stocks) {
_stocks = [[NSMutableArray alloc]init];
}
[_stocks addObject:s];
}
-(float)valueOfPort {
//take sum of all the stocks in this port
float sum = 0;
for (BNRStockHolding *s in _stocks) {
sum += [s valueInDollars];
}
return sum;
}
#end

The -addStock: method is a method of the BNRPortfolio class. You are attempting to call it on an instance of NSMutableArray. There's no such method on NSMutableArray, which is what the compiler is complaining about.
Apparently, you want to allocate and initialize an instance of BNRPortfolio and then call -addStock: on that.

Related

Do I need to override the getter for a property to return an immutable copy?

Suppose that I have a class that holds a mutable array. I want to make sure that if other classes ask for the array they will get a non-mutable type, but in the owning class, it is actually an instance of NSMutableArray, so that I can add and remove items.
#import "Person.h"
#class Asset;
#interface Employee : Person
{
NSMutableArray *_assets;
}
#property (nonatomic,copy) NSArray *assets;
-(void)addAssets:(Asset *)a;
The question is, do I have to modify the accessor methods into something like this, or will it automatically behave like I want?
#import "Employee.h"
#import "Asset.h"
#implementation Employee
/* Accessors for assets properties
-(NSArray *)assets
{
return [_assets copy];
}
-(void)setAssets:(NSArray *)assets
{
_assets = [assets mutableCopy ];
}
*/
-(void)addAssets:(Asset *)a
{
//is assets nil?
if (!_assets) {
//Create Array
_assets = [[NSMutableArray alloc]init];
}
[_assets addObject:a];
}
ppalancica's answer is incorrect. The copy attribute means only that the setter will take a copy when the property is set. The synthesized getter will not return a copy. You must implement that behavior yourself:
- (NSArray *)assets
{
return [_assets copy];
}
You might want to make an internal-only accessor that doesn't make a copy. You could also redeclare the property privately; client code will then be contracted to treat the array it requests as immutable.
This code demonstrates that the synthesized getter returns the uncopied object:
#import <Foundation/Foundation.h>
#interface ArrayReturner : NSObject
#property (copy, nonatomic) NSArray * array;
#end
#implementation ArrayReturner
{
NSMutableArray * _array;
}
- (BOOL)myArrayIsIdenticalTo:(NSArray *)otherArray
{
return _array == otherArray;
}
#end
int main(int argc, const char *argv[])
{
#autoreleasepool {
ArrayReturner * a = [ArrayReturner new];
[a setArray:#[#1, #2]];
NSArray * returnedArray = [a array];
// Does not throw
NSCAssert([a myArrayIsIdenticalTo:returnedArray],
#"Returned array is a separate instance.");
}
return 0;
}
Because you already specified the attribute "copy" for the array property, there is no need to override the getter and setter. The compiler will do all the heavy work for you.
If you specify "strong" instead, the getter and setter would look like:
-(NSArray *)assets
{
return _assets;
}
-(void)setAssets:(NSArray *)assets
{
_assets = assets;
}
And that may be a problem.
There is actually a WWDC conference that explains all these details. For NSString properties it is more recommended to use copy, and you can see it a lot like that in the iOS SDK frameworks.

How to init objects in Objective-C [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm unsure how I should initialise the various properties in an objective-C class. Please assume I'm a very new user to Objective-C in your answers...
I have the following classes:
Test class
#interface Test : NSObject
#property (nonatomic, strong) NSString *name;
#end
TestManager class
#interface TestManager : NSObject
#property (nonatomic, strong) NSMutableArray *tests; // array of Test objects (array size unknown until runtime)
#end
Controller class
#interface TestController : NSObject
#property (nonatomic, strong) TestManager *aManager;
-(void)initManager;
-(void)doSomething;
#end
I want to have an method like initManager called:
-(void)initManager
{
// how can I init the aManager which will have an array of Test objects
}
which will automatically allocate an array of objects to be stored inside the manager class so I can do things like:
-(void)doSomething
{
NSString *name = ((Test *)[self.aManager.tests objectAtIndex:0]).name;
}
I'm not even sure that initManager is the correct method to use - is there something built in that always gets called?
Firstly, let's look at the way we can initialize your Test class objects.
You can also write some initialization method for your Test class so instead of this:
Test example = [[Test alloc] init];
example.name = #"s";
you can write something like this:
Test example = [[Test alloc] initWithName:#"s"];
Please note that this is very common for initialization method to return newly created object, hence the initialization method usually returns 'id' type (not void).
This is the implementation for your test class which will be used in examples below.
.h file:
- (id)initWithName:(NSString *)aName;
.m file:
- (id)initWithName:(NSString *)aName
{
self = [super init];
if (self) {
_name = aName;
}
return self;
}
You can initialize your TestController class this way:
.h file:
- (id)initManager;
.m file:
- (id)initManager
{
self = [super init]; //always call the superclass init method when your class inherit from other class
if (self) { // checking if the superclass initialization was fine
_tests = [NSMutableArray array];
[_tests addObject:[[Test alloc] initWithName:#"s"]];
[_tests addObject:[[Test alloc] initWithName:#"l"]];
}
return self;
}
Or something like this:
- (id)initManager
{
self = [super init]; //always call the superclass init method when your class inherit from other class
if (self) { // checking if the superclass initialization was fine
_tests = [NSArray arrayWithObjects:[[Test alloc] initWithName:#"s"], [[Test alloc] initWithName:#"l"]];
}
return self;
}
Like the #Andrew said it is better to use alloc + init. Here are some examples of this syntax:
CGRect rect = CGRectMake(0, 0, 100, 100);
[[UIView alloc] initWithFrame:rect];
[[NSArray alloc] init]
This is the common way to initialize objects. Despite having this mechanism there are also some additional methods (which are in fact static functions) which give the programmer the nice way to initialize objects. Using them u don't have to write keyword 'alloc' so that the code is shorter and easier to read.
[NSArray array] //creates and returns empty array
[NSMutableArray array] //creates and return empty mutable array
[UIButton buttonWithType:UIButtonTypeContactAdd]; //creates and return button
first import header files of test, and test manager class, into controller class
#import Test.h
#import TestManager.h
then in controller class
-(void)initManager
{
TestManager *aTestManager = [TestManager new];
Test *test1 = [Test new];
Test *test2 = [Test new];
[aTestManager.tests addObject:test1];
[aTestManager.tests addObject:test2];
}
Let's start at the top. You probably can and should make the name readonly.
(Demos assume ARC is enabled)
#interface Test : NSObject
#property (nonatomic, readonly) NSString *name;
// and then simply initialize name:
- (instancetype)initWithName:(NSString *)pName;
#end
NSString properties should be copied:
#implementation Test
- (instancetype)initWithName:(NSString *)pName
{
self = [super init];
if (nil == self) return nil;
// copy the NSString:
// don't use getters/setters in initializers or -dealloc
_name = pName.copy;
return self;
}
#end
Similarly readonly:
#interface TestManager : NSObject
#property (nonatomic, strong, readonly) NSMutableArray *tests; // array of Test objects (array size unknown until runtime)
#end
#implementation TestManager
- (id)init
{
self = [super init];
if (nil == self) return nil;
// just initialize readonly tests:
_tests = NSMutableArray.new;
return self;
}
#end
Then TestController could probably use a readonly TestManager and borrow the form used above. Otherwise, it can be readwrite, if needed.
// don't declare/implement an instance method
// which has the prefix -init*, like initManager. renamed.
- (void)resetManager
{
// if readonly is ok, then just create it in the initializer.
// otherwise, if you need the ability to set the manager in the controller,
// then declare the property readwrite and:
self.testManager = TestManager.new;
// note: aManager is not a good name. renamed to testManager.
}
- (void)doSomething
{
assert(self.testManager && "did you forget to init the manager?");
Test * test = [self.testManager.tests objectAtIndex:0];
NSString * name = test.name;
...
}
This is far from covering all initialization cases in ObjC, but it is a start.

NSArray getter method not returning right value

I'm making a program where one Class (classA) generates a random number and adds it to a mutable array. A view controller (viewControllerA) calls a method from classA and receives the array of random numbers and stores it in its own array.
I have another class (classB) that needs the same array. After viewcontrollerA is finished doing what it needs to do with the array, it calls the setter method for an array in classB. I call NSLog in the setter and getter methods for the array in classB to check to see if it loads.
-(void)setRandomNumberArray:(NSArray *)randomNumberArray{
_randomNumberArray = randomNumberArray;
NSLog(#"%# setter", _randomNumberArray);
}
-
-(NSArray *)randomNumberArray{
if (!_randomNumberArray) {
_randomNumberArray = [[NSArray alloc] init];
}
NSLog(#"%# getter", _randomNumberArray);
return _randomNumberArray;
}
When I call the setter method in viewControlerA, NSLog returns the value of the array.
When I call the getter method in viewControllerB, NSLog prints nothing from the getter method.
2012-05-29 23:57:43.589 SwipeGame[8603:f803] (
) getter
It's obviously setting the array but not retaining it for when i want to get it. What is going on? I've tried multiple other techniques and it always sets the array but doesn't retain it for when i want to "get" the array.
the property for my array is set to retain btw..
UPDATE:
Yes I am using ARC. my property declaration is:
#property (nonatomic, strong) NSArray *randomNumberArray
SOLVED:
Thanks for all your help! It was a problem with instances.
Your setter method does not mention viewControllerB. You are just setting an internal variable. How is viewControllerB going to know about the array having been set?
The easiest way is to just use #properties and #synthesize:
// in A
viewControllerB.array = _array;
As for the retain question: if you use ARC you should not worry about it.
Do you use ARC?
ARC version bellow:
#interface Foo : NSObject {
NSMutableArray *_randomNumberArray;
}
#property (nonatomic, strong) NSMutableArray *randomNumberArray;
#end
#implementation Foo
#synthesize randomNumberArray = _randomNumberArray;
- (void)setRandomNumberArray:(NSMutableArray *)randomNumberArray {
_randomNumberArray = randomNumberArray;
NSLog(#"%# setter", _randomNumberArray);
}
- (NSMutableArray *) randomNumberArray {
if ( _randomNumberArray == nil )
_randomNumberArray = [[NSMutableArray alloc] init];
NSLog(#"%# getter", _randomNumberArray);
return _randomNumberArray;
}
#end
Not ARC version bellow:
#interface Foo : NSObject {
NSMutableArray *_randomNumberArray;
}
#property (nonatomic, strong) NSMutableArray *randomNumberArray;
#end
#implementation Foo
#synthesize randomNumberArray = _randomNumberArray;
- (void)setRandomNumberArray:(NSMutableArray *)randomNumberArray {
[_randomNumber release];
_randomNumberArray = randomNumberArray;
[_randomNumberArray retain];
NSLog(#"%# setter", _randomNumberArray);
}
- (NSMutableArray *) randomNumberArray {
if ( _randomNumberArray == nil )
_randomNumberArray = [[NSMutableArray alloc] init];
NSLog(#"%# getter", _randomNumberArray);
return _randomNumberArray;
}
- (void)dealloc {
[_randomNumberArray release];
}
#end
If you are creating this random number array using an NSMutableArray, and passing that to the setter, the array could be mutated later by the caller (e.g. all items removed) and the array can change from under your feet.
For types like NSArray and NSString which have mutable subtypes, you should declare your property as copy instead of strong. This will ensure the array passed to you cannot be mutated at a later date by somebody else. Copy performance is not a problem because the regular immutable types handle copies very efficiently.

Cannot add object to an NSMutableArray array

#interface SimataDetailViewController ()
#property Simata *simata;
#property (nonatomic, copy) NSMutableArray *simataList;
#end
#implementation SimataDetailViewController
#synthesize simataDataController=_simataDataController;
#synthesize category=_category;
#synthesize simata=_simata;
#synthesize simataList=_simataList;
#pragma mark - Managing the detail item
- (void) getSimataForCategory: (NSString *) inputCategory {
unsigned count = [self.simataDataController.masterList2 count];
while (count--) {
if ([[[self.simataDataController objectSimataInListAtIndex:count] categoryCode] isEqual:inputCategory]){
self.simata= [self.simataDataController objectSimataInListAtIndex:count];
[self.simataList addObject:self.simata];
}
}
NSLog(#"count, %u", [self.simataList count]);
}
Hello this is my first post, so please be patient.
I am trying to add object self.simata to array self.simataList but the array stays with zero objects. The object self.simata is not nil and I don't get any error.
What am I doing wrong?
Are you sure you have created an instance of the array before you use it?
self.simataList =[NSMutableArray array];
It could also be your self.simata being nil...
EDIT
You could create the array instance in the default init method:
-(id)init
{
self = [super init];
if(self)
{
//do your object initialization here
self.simataList =[NSMutableArray array];
}
return self;
}
Most likely self.simataList is nil. Try NSLogging self.simataList itself, rather than its count.

Error: "Unrecognized selector" when using addObjects: on an NSMutableArray

When executing
[self.blockViews addObject:curBlockView];
I get an error
2011-07-01 13:35:26.240 Block Breaker[42061:207] -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x4e037a0
I am pretty new to Objective-C. Is it something in my init method?
//
// GameEngine.h
// Block Breaker
//
// Created by Chris Muench on 7/1/11.
// Copyright 2011 N/A. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface GameEngine : NSObject {
NSMutableArray *blockViews;
int numBlockRows;
int score;
}
#property (nonatomic, copy) NSMutableArray *blockViews;
#property int numBlockRows;
#property int score;
- (void) setup;
- (void) setupBlocks;
#end
//
// GameEngine.m
// Block Breaker
//
// Created by Chris Muench on 7/1/11.
// Copyright 2011 N/A. All rights reserved.
//
#import "GameEngine.h"
#import "Block.h"
#import "BlockView.h"
#implementation GameEngine
#synthesize blockViews;
#synthesize numBlockRows;
#synthesize score;
- (id) init
{
if ((self = [super init]))
{
self.blockViews = [[NSMutableArray alloc] init];
self.numBlockRows = 2;
self.score = 0;
}
return self;
}
- (void) setup
{
[self setupBlocks];
}
- (void) setupBlocks
{
float blockWidth = 10;
float blockHeight = 10;
float rowSpacing = 2;
float colSpacing = 2;
float currentX = 0;
float currentY=10;
float screenWidth = 200;
for (int rowCounter=0;rowCounter<self.numBlockRows;rowCounter++)
{
while(currentX <=screenWidth)
{
Block *curBlock = [[Block alloc] initWithWidth:blockWidth height:blockHeight];
BlockView *curBlockView = [[BlockView alloc] initWithFrame:CGRectMake(currentX, currentY, curBlock.width, curBlock.height)];
curBlockView.block = curBlock;
[self.blockViews addObject:curBlockView];
currentX+=blockWidth+colSpacing;
[curBlock release];
[curBlockView release];
}
currentX=0;
currentY+=blockHeight+rowSpacing;
}
}
#end
When you copy an NSMutableArray using the copy method, or a synthesized setter for which the property was specified as copy, you get an immutable copy, which means you essentially end up with a plain NSArray.* There is a method mutableCopy which will preserve the mutability, but I don't believe there's any way to specify that for a property.
If you want your array to be mutably copied when you set it, you'll have to write your own setter method, and specify that method in the property declaration.
#property (nonatomic, copy, setter=setBlockViewsByCopying) NSMutableArray * blockViews;
- (void) setBlockViewsByCopying: (NSMutableArray *)newBlockViews {
NSMutableArray * tmp = [newBlockViews mutableCopy];
[blockViews release];
blockViews = tmp;
}
A side note, as #InsertWittyName mentioned in a comment, your code initializing blockViews will create a leak, because you have two claims of ownership on the array you're creating -- one for the alloc and one for the retain or copy that you get using the property:
self.blockViews = [[NSMutableArray alloc] init];
// ^ One claim ^ Two claims
// Only one release later, when the property is set again!
// LEAK!
You should instead do:
self.blockViews = [NSMutableArray array];
// Creates an object you don't own which you then make one claim
// on using the setter
*Though as it is a class cluster, you really get some unspecified concrete subclass.
Copy should retain, If I remember correctly.
Anyway using copying cannot be optimal, every time you call the accessor (set), you got a copy, and, as well pointed out above, is immutable.
Adding is fine, but remember to release the array when done.
Did you retain your array ??