Understanding Objective-C method value passing - objective-c

Lets say I have in viewDidLoad:
NSMutableArray *entries = [NSMutableArray array];
[self doSomethingWithArray:entries];
NSLog(#"%#", entries);
Then in method I have:
- (void)doSomethingWithArray:(NSMutableArray *)entries
{
// create some custom data here, lets say - Something *something...
[entries addObject:something];
}
How is it possible that entries (one at the top) now (after method is finished) contain object something, since object "something" is not added to property or instance variable, and nslog will log class "Something" ? And doSomethingWithArray doesn't return anything since its "void".
I have encountered this for first time and dunno if there is any name of this appearance ?
I have seen this for second time in some examples and really dunno how its done.
If anyone could explain this a bit whats happening here I would be very very grateful.
Thank you a lot.

Because Objective-C instances are passed by reference (as you can tell by the * pointer syntax). You basically pass the address of the array to the doSomethingWithArray: method. In that method you add something to the array referenced by that address. And of course once the method returns, your array will contain that new object.

When you are adding the something object to the array, the array always retains it i.e it maintains copy of the Something object.
So NSLog prints the something.
Hope that helps.

Related

Assigning unarchived object to a different object works, why/how?

Assigning an archived dictionary object but unarchived and assigned to an array does not seem to produce an exception and the logged out data from the array is that of the dictionary and not in array format at all if it was possible to assign to a different return type, kinda link casting but instead your unarchiving and assigning directly. Why does this work? if anyone can explain it much more deeply it would bring to light a better understanding of the unarchiving process and the way it works.
NSDictionary *glossary = #{
#"Abstract class": #"some class",
#"Adopt": #"borrow",
#"archiving": #"Storing an object for later use"
};
if([NSKeyedArchiver archiveRootObject:glossary toFile:#"propertyList2.plist"] == YES)
NSLog(#"Archive success");
else
NSLog(#"Archive unsuccessfull");
NSArray *unArchiving = [NSKeyedUnarchiver unarchiveObjectWithFile:#"propertyList2.plist"];
NSLog(#"%#", unArchiving);
//for (NSString *obj in unArchiving) {
// NSLog(#"%#: %#", obj,unArchiving[obj]);
// }
Welcome to Objective-C! Due to the dynamism of the language, the type of an object reference (AKA variable) really only matters at compile time. At runtime, what the object is matters; what you thought it aught to be when you wrote the code doesn't.
The reason your code for unarchiving works is because the for (X x in Y) syntax is identical for arrays and dictionaries. Similarly, the indexing syntax (a[x]) is identical. All of that is handled at runtime, based on the type of the collection being iterated. As long as the semantics are the same, your code will run without error. Obviously, the moment you try to treat that dictionary as an array (e.g. refer to .lastObject) it will blow up.

Method works correctly when called from within its class but not otherwise

I wrote this function in my RootViewController. appRecord is an object holding an XML attribute.
- (NSString*) getString:(int)row{
AppRecord *appRecord = [self.entries objectAtIndex:row];
NSString *appName = appRecord.appName;
return appName;
}
I want to use this function again by writing this:
RootViewController *record = [RootViewController new];
NSString *appsName = [record getString:0];
NSLOG(appsName);
After I compiled, it didn't return anything, but it works and returns appsName if I use this function inside the RootViewController class ([self getString:0]), so I know there is no problem with the function. When I tried to change return appName to return #"test", it worked and returned something when I accessed it from the other class.
This means there is a problem with this appRecord; it was returned inside the class but returns nothing when I access it from another class.
How to solve this problem?
You are getting your AppRecord from self.entries in the instance of RootViewController record. This means unless your new initializer initializes and populates its entries variable (array by the looks of things) before you call getString:, it won't be able to return anything since that array is empty.
Beyond that, I don't know if you want your getString:(int) method to access a different array, or if the problem is in your initialization of RootViewController (more likely)
Maybe I don't understand your question, but if you are relying on the NSLog statement to see if the function is returning anything, you should change it to:
NSLog(#"%#", appsname);

Why do I get EXC_BAD_ACCESS?

I have following code, which executes on button press. At first it works as expected but second time onwards application hangs and I get EXC_BAD_ACCESS signal.
- (IBAction) comicDetailsPressed:(id)sender {
static IssueProperties *props = nil;
if (props == nil) {
props = [ComicDataParser
parseComicForUrl:#"http://dummy.com/Jan.xml"];
}
NSLog(#"%d", [props totalPages]);
totalPages.text = [NSString stringWithFormat:#"%d", [props totalPages]];
}
You didn't say what line it's crashing on, which will mean answers will have to be speculative.
You have a static pointer to a IssueProperties object, but when you assign to it, you aren't using retain. You probably should.
This is assuming that the return value from parseComicForUrl: is a IssueProperties object or a subclass.
I'm assuming that the text property is an NSString set to copy and not retain. If not, it should be.
You need to retain the object you get back from +parseComicForUrl:. Also, why don't you use an instance variable for props?
Without a lot more context, it is going to be impossible to answer for sure, but my first thought would be this:
your static IssueProperties *props would not be nil the second time around. Instead, it would have the value that [ComicDataParser parseComicForUrl] returned.
My guess is that the ComicDataParser is autoreleaseing the response, and so the second time around you have a pointer that is not nil, but is now pointing to an already released object, which is invalid.
If I am right, you need a retain somewhere.

Passing arguments by value or by reference in objective C

I'm kind of new with objective c and I'm trying to pass an argument by reference but is behaving like it were a value. Do you know why this doesn't work?
This is the function:
- (void) checkRedColorText:(UILabel *)labelToChange {
NSComparisonResult startLaterThanEnd = [startDate compare:endDate];
if (startLaterThanEnd == NSOrderedDescending){
labelToChange.textColor = [UIColor redColor];
}
else{
labelToChange.textColor = [UIColor blackColor];
}
}
And this is the call:
UILabel *startHourLabel; // This is properly initialized in other part of the code
[self checkRedColorText:startHourLabel];
Thanks for your help
Objective-C only support passing parameters by value. The problem here has probably been fixed already (Since this question is more than a year old) but I need to clarify some things regarding arguments and Objective-C.
Objective-C is a strict superset of C which means that everything C does, Obj-C does it too.
By having a quick look at Wikipedia, you can see that Function parameters are always passed by value
Objective-C is no different. What's happening here is that whenever we are passing an object to a function (In this case a UILabel *), we pass the value contained at the pointer's address.
Whatever you do, it will always be the value of what you are passing. If you want to pass the value of the reference you would have to pass it a **object (Like often seen when passing NSError).
This is the same thing with scalars, they are passed by value, hence you can modify the value of the variable you received in your method and that won't change the value of the original variable that you passed to the function.
Here's an example to ease the understanding:
- (void)parentFunction {
int i = 0;
[self modifyValueOfPassedArgument:i];
//i == 0 still!
}
- (void)modifyValueOfPassedArgument:(NSInteger)j {
//j == 0! but j is a copied variable. It is _NOT_ i
j = 23;
//j now == 23, but this hasn't changed the value of i.
}
If you wanted to be able to modify i, you would have to pass the value of the reference by doing the following:
- (void)parentFunction {
int i = 0; //Stack allocated. Kept it that way for sake of simplicity
[self modifyValueOfPassedReference:&i];
//i == 23!
}
- (void)modifyValueOfPassedReference:(NSInteger *)j {
//j == 0, and this points to i! We can modify i from here.
*j = 23;
//j now == 23, and i also == 23!
}
Objective-C, like Java, only has pass-by-value. Like Java, objects are always accessed through pointers. "objects" are never values directly, hence you never assign or pass an object. You are passing an object pointer by value. But that does not seem to be the issue -- you are trying to modify the object pointed to by the pointer, which is perfectly allowed and has nothing to do with pass-by-value vs. pass-by-reference. I don't see any problem with your code.
In objective-c, there is no way to pass objects by value (unless you explicitly copy it, but that's another story). Poke around your code -- are you sure checkRedColorText: is called? What about [startDate compare:endDate], does it ever not equal NSOrderedDescending? Is labelToChange nil?
Did you edit out code between this line
UILabel *startHourLabel;
and this line?
[self checkRedColorText:startHourLabel];
If not, the problem is that you're re-declaring your startHourLabel variable, so you're losing any sort of initialization that was there previously. You should be getting a compiler error here.
Here are the possibilities for why this doesn't work:
the label you pass in to checkRedColorText is not the one you think it is.
the comparison result is always coming out the same way.
... actually, there is no 3.
You claim you initialised startHourLabel elsewhere, but, if it is a label from a nib file, you should not be initialising it at all. It should be declared as an IBOutlet and connected to the label in the nib with interface builder.
If it is not a label in the nib i.e. you are deliberately creating it programmatically, you need to check the address of the label you initialise and check the address of the label passed in to checkRedColorText. Either NSLog its address at initialisation and in checkRedColorText or inspect it with the debugger.

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

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
}