How do I set an uninitialized value in Objective C? - objective-c

This question might be off base, but here's the problem I'm having:
I'm trying to run an example from the iPhone SDK, and I'm running into an access violation. I've traced down some behaviour that I think is suspicious.
Here's what the class looks like:
#interface TableViewCell : UITableViewCell {
#private
UILabel *_earthquakeLocationLabel;
UILabel *_earthquakeDateLabel;
UILabel *_earthquakeMagnitudeLabel;
UIImageView *_magnitudeImageView;
}
When I set a breakpoint in
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier
And look at "self" in the debugger, it shows that _earthquakeDateLabel is "0x12", _earthquakeMagnitudeLabel is 0x1000, and the other two are zero. If I try to assign to either of the nonzero ones, I get an access violation.
I'm guessing what's happening is that these have bogus values in them, and when I try to assign to them, that tries to decrement a reference on the bogus value and blows up. But as I said, I'm fairly new to Objective C, so I may be off base.
So my question is, is there anything special about initializing these values that I should be doing? Or any way to assign to the value when it has a bogus value in it?
Some additional information:
So if I assign nil to _earthquakeDateLabel and _earthquakeMagnitudeLabel in initialize, that fixes the problem. But I don't understand why the object is created with values in those fields; I expect them to be nil. The object is being created on the heap:
TableViewCell *cell = [[[TableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];

It sounds like the self pointer is bogus. The values 0x12 and 0x1000 are integer constants; they're definitely NOT pointers to valid memory addresses. The fact that you are seeing those values indicates that something is wrong, and attempting to manipulate them in any way (reading or writing them) will result in badness. In this case, you're getting an access violation because you're trying to write to memory addresses 0x12 and 0x1000, which are invalid addresses.
How are you creating the TableViewCell objects? Are you doing TableViewCell *myCell = [[TableViewCell alloc] initWithFrame:...]? That is the correct way.
Now I'm going to invoke my psychic debugger: my guess is that you're forgetting to declare your TableViewCell object with a pointer, i.e. you're declaring it as TableViewCell myCell instead of TableViewCell *myCell (note the presence of the asterisk). This will create the object on the stack instead of the heap, and as a result, it will have garbage in its data members, instead of zeros. Objective-C objects, when properly allocated, will have all of their data members initialized to 0 (or NULL, nil or false, depending on the data type) after a successful call to alloc.

Related

ARC deallocate my NSmutablearray before NSTableview reloaddata

My NSMutableArray lOfSegments, declared as IVAR, get populated correctly. During the debug it shows 4 object in the array.
for (int x=0; [arrayOfSegmentsTcIn count]>x; x++) {
NSDictionary *segmentDic=[[NSDictionary alloc] initWithObjectsAndKeys: [arrayOfSegmentsNumbers objectAtIndex:x],#"segment",[arrayOfSegmentsTcIn objectAtIndex:x],#"tc_in",[arrayOfSegmentsTcOut objectAtIndex:x],#"tc_out", nil];
[lOfSegments addObject:segmentDic];
[myDMXML.segments addObject:segmentDic];
}
[self.xmlTCLable setStringValue:[myDMXML startTimeCode]];
[self.xmlDurationLable setStringValue:[myDMXML duration]];
[self xmlValidationCheck];
NSLog(#"arrayController:%#",[lOfSegments valueForKey:#"segment"]);
[self.tableViewOutlet reloadData];
NSLog list the array correctly but when reloadData is executed the code jumps to
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [lOfSegments count];
}
The array is null.
The Object is initialised in viewDidLoad as
lOfSegments = [[NSMutableArray alloc]init];
Please Help!
First, I recommend making your code more clear here by using self.lOfSegments rather than directly accessing an ivar. (The fact that the ivar lacks a leading _ is very suspicious as well, and raises the question of whether this is even the variable you think it is.)
On the assumption that this is the variable you think it is, and that you have overridden the standard behavior to make the ivar match the property or created explicit ivars (neither of which you should do), there are several common causes for this kind of problem:
The most likely cause is that you called your initialization code prior to viewDidLoad and then viewDidLoad blew away the array. Many things can run prior to viewDidLoad, and viewDidLoad can run more than once (at least this used to be true; I'd have to study whether the view-loading changes in iOS 6 made it guaranteed to be run once.)
You have some other way reset lOfSegments between the time your initialization code ran and the time reloadData ran. If you would reliably use self. then you could override setLOfSegments: so you could log this. Or you could mark this property readonly so you could prevent it. Thats one of many reasons that you should use properties, not ivars.
The setting code failed to run before reloadData. Ensure that the log statement actually printed prior to getting to reloadData and is on the same queue (the queue identifier will be part of the NSLog output in brackets). I don't think this is likely given your description, but it is a common problem.
There are two instances of this object. Make sure that the object that ran your initialization code is the same object that ran reloadData. This is a more common mistake then you may think. Log self and make sure the memory address is the same in both cases.
looks like you have variable with same name lOfSegments in method viewDidLoad. So in viewDidLoad you use stack variable, in numberOfRowsInTableView - instance variable.
Or maybe tableViewOutlete.dataSource pointing on different viewController

ARC and __unsafe_unretained

I think I have a pretty good understanding of ARC and the proper use cases for selecting an appropriate lifetime qualifiers (__strong, __weak, __unsafe_unretained, and __autoreleasing). However, in my testing, I've found one example that doesn't make sense to me.
As I understand it, both __weak and __unsafe_unretained do not add a retain count. Therefore, if there are no other __strong pointers to the object, it is instantly deallocated (with immutable strings being an exception to this rule). The only difference in this process is that __weak pointers are set to nil, and __unsafe_unretained pointers are left alone.
If I create a __weak pointer to a simple, custom object (composed of one NSString property), I see the expected (null) value when trying to access a property:
Test * __weak myTest = [[Test alloc] init];
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: (null)
Similarly, I would expect the __unsafe_unretained lifetime qualifier to cause a crash, due to the resulting dangling pointer. However, it doesn't. In this next test, I see the actual value:
Test * __unsafe_unretained myTest = [[Test alloc] init];
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: Hi!
Why doesn't the __unsafe_unretained object become deallocated?
[EDIT]: The object is being deallocated... if I try to substitute lines 2 - 3 with NSLog(#"%#", myTest); the app crashes (and an overridden dealloc in Test is being called immediately after the first line). I know that immutable strings will continue to be available even with __unsafe_unretained, and that a direct pointer to the NSString would work. I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
When the object is deallocated it is not zeroed. As you have a pointer to the deallocated object and the property value is stored at some offset to that pointer it is possible that storing and retrieving that property value will succeed after deallocation, it is also quite possible that everything will blow up for some reason or other.
That your code works is quite fragile, try debugging it with "Show Disassembly While Debugging" and stepping through, you'll probably hit an access violation, or take down Xcode itself...
You should never be surprised that strange things happen in C, Objective-C, C++ or any of the family; instead reserve your surprise for so few strange things happening!
Because the constant string in objc is a constant pointer to a heap address and the address is still valid.
edited after comment:
Maybe because the memory at the test objects address hasn't been overwritten and still contains that object? Speculating....
You can see when Test is deallocated by implementing its -dealloc method and adding some simple logging.
However, even if Test is deallocated immediately, the memory it occupied in RAM may remain unchanged at the time you call myVal.
#"hi!" produces a static global constant string instance that is, effectively, a singleton. Thus, it'll never be deallocated because it wasn't really allocated in the first place (at least, it really isn't a normal heap allocation).
Anytime you want to explore object lifespan issues, always use a subclass of NSObject both to guarantee behavior and to make it easy to drop in logging hooks by overriding behavior.
Nothing strange thereā€¦
You need to have at least 1 strong reference to object to keep it alive.
Test * anTest = [[Test alloc] init];
Test * __weak myTest = anTest;
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: (Hi)

Problem assigning value obtained from [array objectAtIndex:]

In my Piano class, I have a property (Keys is another custom class)
#property (nonatomic, retain) Keys *lastPlayed;
In one of my Piano methods, I set the value of lastPlayed using an object from an array of Key objects.
self.lastPlayed = [allKeys objectAtIndex:variable];
The above line of code causes the program to crash.
I've noticed that if I hardcode a specific Key object from the allKeys array, then it works fine. Like so:
self.lastPlayed = keyC;
Interestingly, it doesn't crash if I put the crashing code into a different method.
How can I prevent the crash?
EDIT:
I call this method in the Keys class, where my piano is the delegate
[delegate deliverTagwithNameTag:self.tag]
the piano then responds
- (void) deliverTagwithNameTag:(int)nameTag {
self.lastPlayed = [allKeys objectAtIndex:nameTag];
}
You're probably not getting a plain unexplained crash, you're probably raising an exception. You can watch the console to find out which exception you raised. Normally in this sort of situation it'll be something useful to tell you either that you're asking the array for an out-of-bounds value (which could happen if variable were collecting an incorrect value somehow), that the array itself is invalid (which could be a memory allocation problem) or that the thing returned can't be stored as lastPlayed (which would normally indicate you're doing something custom in the setter and getting an unexpected type of class).
So to prevent the crash, check your console and look for one of those problems.

When to use pointers in objective-c

I am learning how to create iPhone apps and I have seen that most of the variables we create store memory addresses (pointers) instead than holding the actual value or object. I also have found out that every time you declare a variable with the pointer char (*) you know that the variable is going to hold the address and whenever you don't use the (*) mark to declare a variable you know that it will hold the value instead than the memory location. But I don't know when to us which. for example I have:
CGFloat someVar = [image1 alpha]; // This variable does not require *
// image 1 is a: IBOutlet UIImageView
and in this other case I have to use a pointer:
UIViewController *someOtherVar = [[UIViewController alloc] init]; // this type of var requires *
It will be nice if I can know when can I use each instead of trying each until project compiles.
The function and method signatures in the headers and documentation will indicate what the type is.
For example, here is how the alpha property is declared for UIView:
#property(nonatomic) CGFloat alpha;
There is no * anywhere, so you know it returns CGFloat and not CGFloat*.
In contrast, the backgroundColor property is declared like this:
#property(nonatomic, copy) UIColor *backgroundColor;
so you know it will return UIColor* (a pointer).
Some things are declared with a type of id, which is always going to be a pointer to an object.
In general, Objective-C objects (types declared with #interface) will always be referenced as pointers, while primitive C types and structs will often (but not always) be passed and returned by value.
Oversimplifying greatly, but it depends on the data being returned.
From your examples, CGFloat is a wrapper for float, which is a primitive C data type. [image1 alpha] returns a CGFloat. UIViewController is an object type. [[UIViewController alloc] init] returns a pointer to this allocated memory, (UIViewController *). Therefore you need to use the pointer.
Pointers can be used in more cases than can be described. Generally speaking, as you are starting out, you typically use pointers for objects. But I encourage you to check the documentation to determine the exact data type. It will provide hints as to the data type returned by a specific method or property.

Releasing after removal from an array & reference pointers

So some where i have a leak which is related to deleting an object under certain circumstances.
Premise:
- I have an NSMutableArray of Tree objects (a Tree object knows how to draw itself).
- I have a reference pointer (Tree *selected) which basically points to whatever tree i last touched.
- Note that the *selected pointer is a weak reference.
Ok, so far so good.
Problem:
The leak arises when i delete a Tree. From the list i make sure the tree being deleted is releasing everything internally before removing it from the array (removing it from the array should automatically call release on it).
What i tried:
I noticed that my Tree *selected pointer is being assigned the touched tree via the self property:
self.selected = tree;
...and by doing this i know that it is being retained. So what i tried to do was call:
[self.selected release];
I called this right after the tree is removed from the array.
...but at which point it crashes essentially stating it was already released.
Question:
Why am i getting this error message? I removed it from the array, however my self.selected pointer still has a retained count, thus shouldn't i be releasing it?
Perhaps I should set it to nil after the removal process?
Or, perhaps I should set it to autorelease BEFORE the removal process?
Don't attempt to micromanage the retaining/releasing of your selected instance variable. If you want selected to be a weak reference, declare it with the assign attribute:
#property(nonatomic, assign) Tree *selected;
That way it will not be retained when you assign a value to it. Conceptually, it will "piggyback" on the reference that your NSMutableArray is keeping. So when you remove something from your NSMutableArray, do it a bit like this:
if (self.selected == toBeRemoved)
self.selected = nil;
[myArray removeObject:toBeRemoved];
If you didn't explicitly specify assign and your property was retaining its contents, you were most likely getting an exception because the next assignment to self.selected after removing an element caused another -release message to be sent to the old value, which had already been released by your [self.selected release] message.
What's [probably] happening is selected points to a deallocated object, not nothing.
I'd try
self.selected = nil;
instead of releasing it explicitly. That should take care of the retain count, too.
basically, i was assigning my weak references using 'self', thus internally calling retain. i removed the 'self' and now things are fine and dandy. :)
thanks!