Objective-C - How to implement an array of pointers in a method declaration - objective-c

Ok, if you take a look at my two previous posts (Link #2 in particular), I would like to ask an additional question pertaining to the same code. In a method declaration, I am wanting to define one of the parameters as a pointer to an array of pointers, which point to feat_data. I'm sort of at a loss of where to go and what to do except to put (NSMutableArray*)featDataArray in the declaration like below and access each object via another pointer of feat_data type. BTW, sorry to be asking so many questions. I can't find some of the things like this in the book I am using or maybe I'm looking in the wrong place?
-(void)someName:(NSMutableArray*)featDataArray;
feat_data *featDataPtr = [[feat_data alloc] init];
featDataPtr = [featDataArray objectAtIndex:0];
Link #1
Link #2

Your declaration looks fine. "NSMutableArray*" is an appropriate type for your parameter. (Objective-C doesn't have generics so you can't declare anything about what's inside the array.)
One problem I see in your code is that you allocate an object for no reason and then throw away the pointer (thus leaking memory).
I don't know what it is that you are trying to do, so here are some things that you can do with an NSMutableArray:
- (void)someName:(NSMutableArray *)featDataArray {
feat_data *featDataPtr = [[feat_data alloc] init];
[featDataArray addObject:featDataPtr]; // add an object to the end
[featDataPtr release];
feat_data *featDataPtr2 = [[feat_data alloc] init];
[featDataArray replaceObjectAtIndex:0 withObject:featDataPtr2]; // replace an existing entry
[featDataPtr2 release];
feat_data *featDataPtr3 = [featDataArray objectAtIndex:0]; // get the element at a certain index
// do stuff with featDataPtr3
}

Related

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.

Is it bad to set up a property in the getter method?

If I have a property, say an NSArray, that's going to be initialized only once for each instance of my class, is there anything wrong with this:
(in the interface)
#property(strong, nonatomic)NSArray *bubbleArr;
(in the implementation)
-(NSArray*)bubbleArr
{
if(!bubbleArr)
{
NSMutableArray *tempBubbArr = [[NSMutableArray alloc] init];
// get filepath for first speech bubble image for page
NSString *speechBubbleImgPath = [[NSBundle mainBundle] pathForResource:
[NSString stringWithFormat:#"speech_%i_0", pageIndex]
ofType:#"png"];
for(int i = 1; speechBubbleImgPath; i++)
{
UIImage *speechBubbleImg = [[UIImage alloc] initWithContentsOfFile:speechBubbleImgPath];
UIImageView *speechBubbleImgView = [[UIImageView alloc] initWithImage:speechBubbleImg];
[tempBubbArr addObject:speechBubbleImgView];
speechBubbleImg = nil;
speechBubbleImgView = nil;
speechBubbleImgPath = nil;
speechBubbleImgPath = [[NSBundle mainBundle] pathForResource:
[NSString stringWithFormat:#"speech_%i_%i", pageIndex, i]
ofType:#"png"];
}
bubbleArr = [[NSArray alloc] initWithArray:tempBubbArr];
tempBubbArr = nil;
}
return bubbleArr;
}
I've never used custom accessor methods, but this seems like a clean way to set it up, so I don't have to set up each property in my viewDidLoad or elsewhere, and don't have to worry about it being nil. I don't recall ever actually come across code that does this. Is this the recommended way to do it? Also, I'll always want to use self.bubbleArr to make sure this method is called, right?
This is a totally valid way of setting up your property. Apple does this sort of thing very often in their sample code as well as in their project templates. Look, for example, at the core data stack setup in a newly created core data iOS project. As #WendiKidd noted, you have to access your variable through the accessors all the time to make sure this works nicely (which is probably what you should be doing anyway).
In particular, this is a good way of implementing a property of a class that can really only return one thing (which, from your comment, it sounds like is what you're trying to do). If that's your goal, here are some guidelines to follow:
Declare your property as readonly
Declare it in the public header if it should be publicly accessible, or in a class extension in the .m file if it should be "private"
If it can/should be backed by a variable, synthesize the ivar and overwrite the getter as you've done
If it shouldn't/doesn't have to be backed by a variable, declare the property as #dynamic in the implementation and overwrite the getter
Only access your ivar through the accessor
Declaring the variable as dynamic in the 4th point will signal to anyone that looks at your code that you've likely written a custom accessor for that property.
The technique of waiting until you need the contents of a variable to initialize it is called 'lazy loading', and is a valid technique. I'm not sure about doing it inside the getter, though.
I think the problem is described exactly by the last line of your post--yes, you would always have to make sure you called the getter method when you wanted to reference the object, even inside the class itself. It's easy to make mistakes and not do that properly, and it's an especially bad idea if your code could be passed on to another developer in the future. They are absolutely not going to expect that you've set things up this way, and could end up with problems when they expect to be able to access the variable as normal. It is common and accepted practice to initialize member variables inside viewDidLoad.
So yes--this is technically possible, though not a very sound setup, design-wise. I would recommend strongly against it. But if you simply want to know if it will function, the answer is yes.
Yes, if you don't use self.bubbleArray OR [self bubbleArray] you will not be invoking that method.
Other than that, totally a fine way of managing a property's instantiation.
It seems like a clean solution, it feels a bit like lazy loading. But I am not sure I would do it like this.

Can I reuse my pointer after it's been added to a mutable array?

Let's say I've got an array with strings.
NSArray *names = [NSArray arrayWithObjects: #"One", #"Two", #"Three", nil];
What I want is to initiate objects of some custom class and them add them to a mutable array. I'm using a custom init method that takes a string argument.
To be more specific, I want to [SomeClass alloc] initWithName: aName] and add the resulting object to a NSMutableArray.
I'm thinking of using Objective-C fast enumeration. So what I get is:
NSMutableArray *objects = [NSMutableArray arrayWithCapacity: [names count];
for (NSString *name in names) {
[objects addObject: [[[SomeClass alloc] initWithName: name] autorelease]];
}
The problem is that I can't add nil to the array and I don't like exception handling. However, my initiation method may return nil. So I decide to check first before adding (prevention). My new for-in-loop is:
SomeClass *someObject;
for (NSString *name in names) {
someObject = [[[SomeClass alloc] initWithName: name] autorelease];
if (someObject) {
[objects addObject: someObject];
}
}
Now, instead of immediately passing the new object to the array, I'm setting up a pointer someObject first and then passing the pointer to the array instead.
This example raises a question to me. When I someObject = [[[SomeClass alloc] initWithName: name] autorelease] in the loop, do the existing objects (which are added using the same pointer) in the array change too?
To put it in other words: does the addObject: (id)someObject method make a new internal copy of the pointer I pass or do I have to create a copy of the pointer — I don't know how — and pass the copy myself?
Thanks a lot! :-)
It's fine to reuse someObject; if you think about it, you're already reusing name each time you go through the loop.
-addObject: may or may not copy the object that you pass in. (It doesn't -- it retains the object rather than copying it, but it's conceivable that some NSMutableArray subclass could copy instead.) The important thing is that this code really shouldn't care about what -addObject: does.
Also, don't lose sight of the distinction between a pointer and the object that it points to. Pointers are just references, and a pointer is copied each time you pass it into a method or function. (Like C, Objective-C passes parameters by value, so passing a pointer into a method results in putting the value of the pointer on the stack.) The object itself isn't copied, however.
Short answer: no, you don't have to worry about reusing someObject.
Slightly longer answer: the assignment—someObject = ... assigns a new pointer value to the someObject variable; addObject: is then getting that value, not the address of someObject itself.
I think you're getting confused in the concept of pointer here. When you say someObject = [[[SomeClass alloc] init... you are basically pointing the someObject pointer to a new object. So to answer your question- your current code is fine.
As for whether arrays maintain copies of the objects added to them - NO, the array retains the object you add to it. However, that doesn't matter to your code above.
Three20 provides the answer!

Enumeration and removing a particular object from NSMutableArray

I'm having trouble removing items from my NSMutableArray. I'm extremely new to Objective-C, so please bear with me.
So far, I have the following: I'm trying to remove a line from the array if it has certain text inside. I cannot do this while fast-enumerating, so I'm trying to store the index, for removal after the enumeration has finished. However, I'm being told that this makes a pointer from an integer without a cast. Confused!
//First remove any previous Offending entry.
//Read hostfile into array.
NSString *hostFileOriginalString = [[NSString alloc] initWithContentsOfFile:#"/etc/hosts"];
NSMutableArray *hostFileOriginalArray = [[hostFileOriginalString componentsSeparatedByString:#"\n"] mutableCopy];
NSInteger hostFileOffendingLocation;
//Use a for each loop to iterate through the array.
for (NSString *lineOfHosts in hostFileOriginalArray) {
if ([lineOfHosts rangeOfString:#"Offending"].location != NSNotFound) {
//Offending entry found, so remove it.
//[hostFileOriginalArray removeObject:lineOfHosts];
hostFileOffendingLocation = [hostFileOriginalArray indexOfObject:lineOfHosts];
//NSLog(#"%#", hostFileOffendingLocation);
}
}
//Release the Array.
[hostFileOriginalArray release];
//Remove offending entry from Array.
[hostFileOriginalArray removeObject:hostFileOffendingLocation];
My real question is why are you releasing your array before modifying it
try moving
[hostFileOriginalArray release];
to after
[hostFileOriginalArray removeObject:hostFileOffendingLocation];
You can do this without the loop by calling [hostFileOriginalArray removeObjectIdenticalTo:#"Offending"];
Note that it will remove multiple instances of the offending object, but that looks like what you want anyway. It will also do the operation in a fast way, without you having to worry about the implementation detail of which loop to use.
As a general rule (especially with the really commonly used objects like containers and NSString), check the class reference to see if Apple already has a way of doing what you want to do. It makes your code more readable to other Cocoa users (including future you), and reduces code maintenance- you're now leaving it up to Apple to add new technologies like Fast Enumeration to their code, and you get it for free when you link against new versions of the SDK.
Also, you should probably return hostFileOriginalArray at the end of the function, so it can do something useful- you can return it as an autoreleased object.
//Remove offending entry from Array.
[hostFileOriginalArray removeObjectAtIndex:hostFileOffendingLocation];
//Release the Array.
[hostFileOriginalArray release];
you should have been getting compiler warnings... take a look at them, they are usually very helpful, I always try to have 0 warnings... that way I know where I have done something careless.

Add NSMutableArray to another NSMutableArray

Hey! I've been trying to add an array to an NSMutableArray using the addObject syntax. The app crashes without any explicit mention of an error. Could you please tell me how this is done?
I have a class Stack that creates an array. So I call that class using an instance called tem that I have created. Hence, [self tem] is my call to the array. Through the program I merely add UILabels to the array(I know you'd suggest adding a string and then changing to UILabels, but I need to do it this way) and towards the end, I'd like to add this array to my 'list' array.
-(Stack *)tem {
if(!tem)
tem=[[Stack alloc]init];
return tem;
}
-(void)viewDidLoad{
//code
list=[NSMutableArray arrayWithArray:[self list].array];
}
-(IBAction)opPr:(UIButton *)sender
{
if([[[sender titleLabel]text] compare: #"="]==0) {
//code
UILabel *t=[[UILabel alloc]init];
//complete creating label
[[self tem]push:t];
//add above label to tem array
[list addObject:[self tem].array];
[table reloadData];
}
}
OK, this answer got all cluttered with edits. I've edited it to be more clear, at the possible expense of understanding the thread of how we arrived at the final answer.
The final answer
The answer was to change:
list=[NSMutableArray arrayWithArray:[self list].array];
To:
list = [[NSMutableArray alloc] init];
(...Which isn't really the best way to create an NSMutableArray, but it may work anyway.)
This was based on an erroneous version of the asker's code
Based on your comment that it crashes after [list addObject:[self tem].array];, I must conclude that your instance variable list is of type Stack*, and that Stack is not a subclass of NSMutableArray. If so, that's your issue.
In that case, changing that line to [[list array] addObject:[self tem].array]; should fix it.
This is just good advice
As an aside, NSMutable array is perfectly capable of acting as a stack without modification. Example:
NSMutableArray* ar = [NSMutableArray arrayWithCapacity:someCapacity];
// Push
[ar addObject:narf]; // narf being some object reference
// Pop
id popped = [ar lastObject];
[ar removeLastObject];
If you want to encapsulate stack behavior in a semantically consistent way, you can add push and pop methods to NSMutableArray using a category. This would make your code simpler and less prone to error.
This was my first stab at answering the question, before any code had been posted
This is a stab in the dark since you've not posted any code. BUT. One way to accomplish that with arrays is by creating arrays using [NSArray arrayWithObjects:obj obj ... nil] and omitting the nil terminator on the list of objects. Are you by any chance doing that?