Can I create an array prior to running a method? - objective-c

int indexOfArray = 0;
-(void)pushNumber:(double)number{
if(self.numArray == NULL)
self.numArray = [NSMutableArray array];
indexOfArray = self.numArray.count;
[self.numArray insertObject:[NSNumber numberWithDouble:number] atIndex:(indexOfArray)];
indexOfArray--;
}
This is currently what part of my code looks like for a push method I am doing. The way I want to set it up is to push the number to the end of the array because that will allow me to pop the first number I pushed in using lastObject. I have experience in Java, but I have been making my way to Objective-C on my own, hence I am not sure if what I am doing is right. What I do know right now is that ever time this method runs indexOfArray will reset its count, and I don't want that. I want to be able to initialize the array before the method so I can have a constant size to start with which I can then decrement each time this method is called.
To be clear, this is for a calculator app I am making in my free time. I want to be able to reset the indexOfArray every the user presses a number after an operator, when the user presses clear, or when the user presses the = button.

If you're looking for an equivalent of a constructor in Java, override -init. Example:
- (id)init
{
self = [super init]
if (!self) {
return nil;
}
self.numArray = [NSMutableArray array];
return self;
}
However, as others have said, if your class is just a stack, it might be easier just to use an NSMutableArray, as it has methods for stack-like access.
Also, if int indexOfArray = 0; is really defined next to a method as it looks, you've got a global variable not an instance (/member) variable, which you might think you're declaring.
(if this stuff is new to you, I recommend reading Apple's Cocoa Core Competencies and Programming With Objective-C articles)

Related

Cocoa user interface loading problems

I'm a noob on objective C so I'm sorry if I don't use the right terms to describe my problem but I need a hand and SO is my least resource!
I'm trying to interface an Arduino balance with my mac with an objective C software in which I've got a Nib file and a controller one: to do that I use ORSSerialPort which runs ok.
I'm experiencing some problems while loading a NSCombobox (the one with I make the user able to choose the serial port) after the user interface loading: in my controller class I have a method called "InitializeView" which calls this method:
-(void)RefreshSerialPortsInComboBox{
//Clear all existing elements
for (int i = 0; i < [self.serialPortsComboBox numberOfItems]; i++) {
[self.serialPortsComboBox removeItemAtIndex:i];
}
//Reload the serial ports list
NSArray *availableSerialPorts = [[NSArray alloc] initWithArray:[serialPortManager availablePorts]];
//Reload the Combobox elements with the new serial ports list
for (int i = 0; i < [availableSerialPorts count]; i++) {
[self.serialPortsComboBox addItemWithObjectValue:[[availableSerialPorts objectAtIndex:i] valueForKey:#"_path"]];
[self InsertTextInBufferTextView:#"Elemento creato...\n"];
}
}
The problem is that if I call this method in my controller object's init method, this doesn't work and the combobox items list is still empty: take note that the controller is instantiated by Interface Builder.
I tried to link the above method to a button and it works so it seems to be a matter of loading priority, it seems that I'm going to call the method before loading the ui objects or making them ready to be worked on...or maybe something else but I don't know what.
Can someone help me?
Another similar alternative just for completeness is to implement the method awakeFromNib, this method is called for all objects represented in a nib file. Its defined in the informal protocol NSNibAwaking
You should call this method in loadView method:
- (void)loadView {
[super loadView];
[self RefreshSerialPortsInComboBox];
}
Your code doesn't work because view and all IBOutlets haven't not initialized in "init" method yet.

How to replicate NSArray memory semantics in a subclass

Question
In my ARC project I have a class that manages objects, called LazyMutableArray. Some of the objects are actually nil, but users of my collection will never know about this; therefore I made it a subclass of NSMutableArray, and it tries to do "the same thing". In particular, objects are retained when added.
Now let's take a look at a memory behavior of other methods. It turns out that the NSArray destruction methods are documented by Apple to be an exception to this rule, in that they release, not autoreleased object.
There is some debate as to whether the combination of addObject: + objectAtIndex: + array destruction is documented by Apple to be never autoreleasing or simply happens to be in the examples I tested and in the example Apple includes.
How can I create in my subclass a method with exact same memory semantics?
Last update
After some thought, I've decided implementation based on NSMutableArray is more appropriate in this case compared to NSPointerArray. The new class, I should note, has the same retain/autorelease pair as the previous implementation.
Thanks to Rob Napier I see that no modification of my objectAtIndex: method would change this behavior, which answers my original question about this method.
On a practical level, several people said that any method can tackle an extra retain/autorelease pair for no reason; it's not reasonable to expect otherwise and not reasonable to try to find out which methods do this and which do not. It's been therefore a great learning opportunity for me on several levels.
Code (based on NSMutableArray) is available at GitHub: implementation, header, test (that's -testLazyMutableMemorySemantics).
Thank you all for participating.
Why I try to subclass NSMutableArray:
Subclassing foundation objects, I agree, is not always an appropriate solution. In tho case I have objects (in fact, OData resources), most of which have subobjects. The most natural class for an array of subobjects is obviously NSArray. Using a different class doesn't seem to make sense to me.
But for an OData collection this "array of sub objects", while, being an NSArray, must have a different implementation. Specifically, for a collection of 1000 elements, servers are encouraged to return collection in batches of (say)20, instead of all at once. If there is another pattern appropriate in this case, I'm all ears.
Some more detail in how I found this
I unit test the hell out of this collection, and values can be put into array, read from the array, and so forth. So far, so good. However, I realized that returning the object increases its retain count.
How do I see it? Suppose I insert two objects into lazy array lazy, one held weakly, one held strongly (*see the code *). Then retain count of weakSingleton is, as expected, 1. But now I read element:
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage"); // line B
And in the debugger I see the retain count go up to 2. Of course, -retainCount may give me wrong information, so let's try to destroy the reference in array by
lazy[0] = nil; // yep, does the right thing
XCTAssertNil(weakSingleton, #"Dropped by lazy array"); // line C <-- FAIL
indeed, we see that weakSingleton is not released.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. The exact source of this pair is not obvious, but seems to come from NSPointerArray -addPointer: (and unfortunately not from ARC's [[object retain] autorelease]). However, I don't want to return an autoreleased object and make method semantics different from its superclass!
After all, the method I'm overriding, NSMutableArray -objectAtIndex:`, doesn't do that; the object it returns will dealloc immediately if an array is released, as noted in the Apple's example. That's what I want: modify the method around line A so that the object it returns does not have an extra retain/autorelease pair. I'm not sure the compiler should even let me do it :)
Note 1 I could turn off ARC for a single file, but this would be my first non-ARC Objective-C code. And in any case the behavior may not some from ARC.
Note 2 What the fuss? Well, in this case I could change my unit tests, but still, the fact is that by adding or deleting line B, I'm changing the result of unit test at line C.
In other words, the described behavior of my method [LazyMutableArray -objectAtIndex] is essentially that by reading an object at index 0, I'm actually changing the retain count of this object, which means I could encounter unexpected bugs.
Note 3 Of course, if nothing is to be done about this, I'll document this behavior and move on; perhaps, this indeed should be considered an implementation detail, not to be included into tests.
Relevant methods from implementation
#implementation LazyMutableArray {
NSPointerArray *_objects;
// Created lazily, only on -setCount:, insert/add object.
}
- (id)objectAtIndex:(NSUInteger)index {
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
// otherwise do something else to compute a return value
// but this branch is never called in this test
[self.delegate array:self missingObjectAtIndex:index];
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
#throw([NSException exceptionWithName:NSObjectNotAvailableException
reason:#"Delegate was not able to provide a non-nil element to a lazy array"
userInfo:nil]);
}
- (void)createObjects {
if (!_objects) {
_objects = [NSPointerArray strongObjectsPointerArray];
}
}
- (void)addObject:(id)anObject {
[self createObjects];
[_objects addPointer:(__bridge void*)anObject];
}
The complete test code:
// Insert two objects into lazy array, one held weakly, one held strongly.
NSMutableArray * lazy = [LazyMutableArray new];
id singleton = [NSMutableArray new];
[lazy addObject:singleton];
__weak id weakSingleton = singleton;
singleton = [NSMutableDictionary new];
[lazy addObject:singleton];
XCTAssertNotNil(weakSingleton, #"Held by lazy array");
XCTAssertTrue(lazy.count == 2, #"Cleaning and adding objects");
// #autoreleasepool {
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage");
XCTAssertEqual(singleton, lazy[1], #"Correct element storage");
// }
lazy = nil;
XCTAssertNotNil(singleton, #"Not dropped by lazy array");
XCTAssertNil(weakSingleton, #"Dropped by lazy array");
The last line fails, but it succeeds if I change first line to lazy = [NSMutableArray new] or if I uncomment #autoreleasepool.
First, I would not make this subclass. This is exactly what NSPointerArray is for. Wrapping that into an NSArray obscures important details that this approach can break. For example, what is the correct behavior for [NSArray arrayWithArray:lazyMutableArray] if lazyMutableArray includes NULLs? Algorithms that assume that NSArray can never include NULL need to be wary of the fact that this one can. It's true that you can get similar issues treating a non-retaining CFArray as an NSArray; I speak from experience that this is exactly why this kind of subclass can be very dangerous (and why I stopped doing that years ago). Don't create a subclass that cannot be used in every case that its superclass can be used (LSP).
If you have a collection with new semantics, I would subclass it from NSObject, and have it conform to <NSFastEnumeration>. See how NSPointerArray is not a subclass of NSArray. This was not an accident. Faced with the same problem, note the direction Apple chose.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. This seems to be because line A under ARC translates to [[object retain] autorelease]. However, I don't want to return an autoreleased object and make caller remember this!
The caller should never assume anything else. The caller is never free to assume that a method does not add balanced autoreleases. If a caller wants the autorelease pool to drain, that is their responsibility.
All that said, there is some benefit to avoiding an extra autorelease if it's not required, and it's an interesting learning opportunity.
I would start by reducing this code to the simplest form, without your subclass at all. Just explore how NSPointerArray works:
__weak id weakobject;
#autoreleasepool
{
NSPointerArray *parray = [NSPointerArray strongObjectsPointerArray];
{
id object = [NSObject new];
[parray addPointer:(__bridge void*)object];
weakobject = object;
}
parray = nil;
}
NSAssert(!weakobject, #"weakobject still exists");
My structure here (such as the extra nesting block) is designed to try to avoid accidentally creating strong references I don't mean to make.
In my experiments, this fails without the autoreleasepool and succeeds with it. That indicates that the extra retain/autorelease is being added around or by the call to addPointer:, not by ARC modifying your interface.
If you're not using this implementation for addObject:, I'd be interested in digging deeper. It is an interesting question, even if I don't believe you should be subclassing this way.
I'm going to elaborate on why I said this "looks a lot like a homework assignment." This will likely earn me many down votes, but it will also server as a good learning case for others who later find this question.
Subclassing NSMutableArray not a goal of a program. It is a means to achieve something else. If I were to venture a guess, I expect you were trying to create an array that lazily creates the object when they are accessed. There are better ways to do this without dealing with memory management yourself.
Here's an example of how I would implement a lazy loading array.
#interface LazyMutableArray : NSMutableArray
- (id)initWithCreator:(id(^)(int))creator;
#end
#interface LazyMutableArray ( )
#property (nonatomic, copy) id (^creator)(int);
#property (nonatomic, assign) NSUInteger highestSet;
#end
#implementation LazyMutableArray
- (id)initWithCreator:(id(^)(int))creator
{
self = [super init];
if (self) {
self.highestSet = NSNotFound;
self.creator = creator;
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
id obj = nil;
if ((index < self.highestSet) && (self.highestSet != NSNotFound)) {
obj = [super objectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
obj = self.creator(index);
[super replaceObjectAtIndex:index withObject:obj];
}
} else {
if (self.highestSet == NSNotFound) {
self.highestSet = 0;
}
while (self.highestSet < index) {
[super add:[NSNull null]];
self.highestSet += 1;
}
obj = self.creator(index);
[super add:obj];
self.highestSet += 1;
}
return obj;
}
Fair Warning: I'm not compiling or syntax checking any of this code. It probably has a few bugs in it, but it should generally work. Additionally, this implementation is missing an implementation of add:, count, removeObjectAtIndex:, insertObject:atIndex:, and possibly replaceObjectAtIndex:withObject:. What I show here is just to get you started.

Using pointers to adjust global objects in objective-c

Ok, so I am working with two sets of data that are extremely similar, and at the same time, these data sets are both global NSMutableArrays within the object.
data_set_one = [[NSMutableArray alloc] init];
data_set_two = [[NSMutableArray alloc] init];
Two new NSMutableArrays are loaded, which need to be added to the old, existing data. These Arrays are also global.
xml_dataset_one = [[NSMutableArray alloc] init];
xml_dataset_two = [[NSMutableArray alloc] init];
To reduce code duplication (and because these data sets are so similar) I wrote a void method within the class to handle the data combination process for both Arrays:
-(void)constructData:(NSMutableArray *)data fromDownloadArray:(NSMutableArray *)down withMatchSelector:(NSString *)sel_str
Now, I have a decent understanding of object oriented programming, so I was thinking that if I were to invoke the method with the global Arrays in the data like so...
[self constructData:data_set_one fromDownloadArray:xml_dataset_one withMatchSelector:#"id"];
Then the global NSMutableArrays (data_set_one) would reflect the changes that happen to "array" within the method. Sadly, this is not the case, data_set_one doesn't reflect the changes (ex: new objects within the Array) outside of the method.
Here is a code snippet of the problem
// data_set_one is empty
// xml_dataset_one has a few objects
[constructData:(NSMutableArray *)data_set_one fromDownloadArray:(NSMutableArray *)xml_dataset_one withMatchSelector:(NSString *)#"id"];
// data_set_one should now be xml_dataset_one, but when echoed to screen, it appears to remain empty
And here is the gist of the code for the method, any help is appreciated.
-(void)constructData:(NSMutableArray *)data fromDownloadArray:(NSMutableArray *)down withMatchSelector:(NSString *)sel_str {
if ([data count] == 0) {
data = down; // set data equal to downloaded data
} else if ([down count] == 0) {
// download yields no results, do nothing
} else {
// combine the two arrays here
}
}
This project is not ARC enabled.
Thanks for the help guys!
Rob
If I understood your problem, you are trying to pass your object as call-by-reference and hoping to work as in C++/C. But Obj-C, although similar but has some different way. You have to dereference the object by using ** (double pointer) as mostly seen in NSError case, which is very rare.
Second ways is: wrap method as a block. Then put the variable in the same lexical scope as the block, and denote it by __block storage type.
Third way can be accessing your object directly by making it an ivar/property or even an singleton.

How to display data in a nscombobox in cocoa?

I have an NSComboBox in my mainmenunib file.
I have created an outlet of combobox "cb" and made a connection of it with my delegate
I also connected delegate and datasource with my delegate.
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{ arr=[NSMutableArray arrayWithObjects:#"a",#"b",#"c",#"d",#"e",#"f", nil];
[cb reloadData];
}
-(NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox{
return arr.count;
}
-(id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)loc{
return [arr objectAtIndex:loc];
}
But when I run the application data is not coming in combobox.
Please help me out as i am new to cocoa programming.
Thanks in advance.
Your approach seems reasonable on the face of it, though using a mutable object as an instance variable is often ill-advised (for reasons wholly unrelated to your issue here, and which we needn't get into at this stage).
There are two things that jump out as possible issues:
1) Are you using ARC? If not, arr is going to disappear from under you because -arrayWithObjects returns an autoreleased object and you have nothing retaining it. If you are using ARC (the default for new project on Lion, I believe), this doesn't apply to you. Plus I would expect you would crash, not just get no data.
2) More likely, you forgot to -setUsesDataSource:YES, which is the flag that tells NSComboBox whether to look at its data source or to use the internal contents approach that #JustinBoo supplied. I believe this defaults to NO, which would cause your exact problem. I don't have Interface Builder in front of me at the moment, but IIRC there is a "uses data source" checkbox that you can check to enable this attribute.
You can add objects using -addItemWithObjectValue to Your NSComboBox like this:
arr = [NSMutableArray arrayWithObjects:#"a",#"b",#"c",#"d",#"e",#"f", nil];
for (int i = 0; i < [arr count]; ++i)
{
[cb addItemWithObjectValue:[arr objectAtIndex:i]];
}
You can see NSComboBox Reference for more information.

How to choose a method based on an element of an NSArray (Objective-C)

I'm writing a sort of calculator app. I have a UIPickerView (1 column) loading data from an NSArray of strings. The user will select one of these (it's selecting which type of calculator to use -- each uses a different method to calculate). The user inputs some things into some UITextFields and then presses a UIButton to do the calculations.
My NSArray is this:
calcNames = [NSArray alloc] initWithObjects:#"first", #"second", #"third", nil];
And my methods are called firstCalc(input1, input2, input3), secondCalc(input1, input2, input3), and so on. (The inputs are coming from the UITextFields.)
When I press the button, I would like to tell it to look at what the selection in the UIPickerView is and run the corresponding method without just typing an if-then statement for each one (it's very inconvenient to do this for reasons specific to my app, which are beyond the scope of this discussion).
So I have already defined a way to determine what the selected calc is:
selectedCalc = [[NSString alloc] initWithString:[calcNames objectAtIndex:row]]
where 'row' is the current selection in the UIPickerView.
Now I have a doCalculations method for when someone presses the UIButton:
-(IBAction)doCalculations:(id)sender {
// save the data input
double input1 = [input1Field.text doubleValue];
double input2 = [input2Field.text doubleValue];
double input3 = [input3Field.text doubleValue];
// do the calculations
int i;
for (i = 0; i < [calcNames count]; i++) {
if (selectedCalc == [calcNames objectAtIndex:i]) {
// do calculations here
double numResult = ??????
// if selectedCalc is "first", I want it to do firstCalc(input 1, input 2, input 3)
// if selectedCalc is "second", I want it to do secondCalc(input 1, input 2, input 3), and so on
// the rest is just for displaying the result
NSString* result = [NSString stringWithFormat:#"The answer is %f", numResult];
[resultLabel setText:result];
}
}
}
So basically, it runs a for loop until it finds which calculator is selected from the UIPickerView and when it finds it, runs the calculations and displays them.
I've been trying to understand if maybe function pointers or selectors (NSSelectorFromString?) are the right things to use here and how to use them, but I'm really struggling to understand where to go after a couple days of reading Apple's documentation, Stack Overflow questions, playing with sample code, and tinkering with my own code.
Sorry if the question is too lengthy, I thought it may be more helpful to others looking for assistance in the future to see the full idea. (At least I know sometimes I'm lost with these question pages.)
I would be very grateful for any assistance,
Ryan
You can dynamically invoke a method using a selector. You could for example have a secondary array to calcNames with selector called calcSelectors:
SEL calcSelectors[] = (SEL[3]){
#selector(first:arg:),
#selector(second:arg:),
#selector(third:arg:)};
Calling the right method would then be as simple as:
[self performSelector:calcSelectors[calcIndex] withObject:arg1 withObject:arg2];
If you need more then 2 arguments, then you also need to mess a bit with a NSInvocation instance to setup the call.
Example 1:
NSString *method=[calcNames objectAtIndex:0];//here play with objectatindex
SEL s=NSSelectorFromString(method);
[self performSelector:s];
which will call this method
-(void)first{
NSLog(#"first");
}
-----------------------------------------
Example 2:
NSString *totalMethodName;
totalMethodName=#"vijay";
totalMethodName=[totalMethodName stringByAppendingString:#"With"];
totalMethodName=[totalMethodName stringByAppendingString:#"Apple"];
SEL s=NSSelectorFromString(totalMethodName);
[self performSelector:s];
will call
-(void)vijayWithApple{
NSLog(#"vijayWithApple called");
}
You can make use of NSInvocation to dynamically bind multiple arguments to a selector. Follow this post to learn it.
If you are going to use NSInvocation you have to define your methods in the objective-C way something like the following.
- (double)firstCalcWithInput1:(double)input1 input2:(double)input2 andInput3:(double)input3;
- (double)secondCalcWithInput1:(double)input1 input2:(double)input2 andInput3:(double)input3;