SIGSEGV when setting block to nil - objective-c

I'm running into a strange crash that's difficult to reproduce. For instance, out of the past 35 runs of the app, this crash happened once. I'm not sure of the exact steps to repro unfortunately.
The crash report shows:
SIGSEGV
Remotely-[NetworkCall handleRequestFinished]
in NetworkCall.m on Line 232
line 232 is:
_startBlock = nil;
and that property is defined as:
typedef void (^NetworkCallStartBlock)();
#property(copy, nonatomic) NetworkCallStartBlock startBlock;
The one time i was able to repro the bug w/ the debugger attached, printing out _startBlock to console showed its value was already nil when attempting to assign nil.
Any thoughts?

_startBlock = nil; change to self.startBlock = nil
the latter one make your block release and safely set the property to nil.

JosephH's comment above was correct. setting the already nil'd value to nil was happening after self was dealloc'd, so it was dereferencing self that was the issue. the dealloc for this class wasn't properly setup to nil everything out, so fixing that solved the issue. Thanks all for the input and tracking it down

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

AXUIElementRef and ARC - Deallocated instances and __bridge vs __bridge_transfer

I get this error on an NSMutableArray:
-[Not A Type release]: message sent to deallocated instance 0x1006e29c0
It happens on this line:
[_array removeAllObjects];
Now, I understand what the error means but not why it happens in this case.
I've added an NSLog right before the above line like so:
NSLog(#"%#", [_array class]);
And this executes normally and with the correct behavior, this is the log:
2013-03-13 11:19:27.366 App[66921:303] __NSArrayM
2013-03-13 11:19:27.367 App[66921:303] *** -[Not A Type release]: message sent to deallocated instance 0x1006e29c0
So right before removing it doesn't seem to be deallocated..
Even if I remove the removeAllObjects call and replace it with the below line, I still get the same error.
_array = [[NSMutableArray alloc] init];
All I'm doing elsewhere in the code is calling [_array addObject:...]
_array is a strong property:
#property (strong) NSMutableArray *array;
The array is first initialized in the init method.
EDIT: I'm adding AXUIElementRefs to this array like so:
[_array addObject:(__bridge_transfer id)elementRef];
I thought the __bridge_transfer brings it over to ARC and I wouldn't need to manage any of that?
What could be the problem?
UPDATE:
Here is a sample project: http://users.telenet.be/prullen/AXMemory.zip
This is a working project as I'm using __bridge and not __bridge_transfer.
This project contains a simple loop that does the same thing over and over again. This is to demonstrate that with just __bridge, the AXUIElementRefs never get released. They stay in memory. That's what I see in the Instruments profiler too. You can also see the memory usage increasing every few seconds via activity monitor.
The AXUIElementRefs were obtained via AXUIElementCopyMultipleAttributeValues - so I would assume that I would have ownership of them? Nevertheless, changing to __bridge_transfer results in the errors above.
If anyone could take a look at this and let me know what I'm doing wrong, that would be much appreciated.
I get an array of properties via the following statement: (line 60).
AXUIElementCopyMultipleAttributeValues(frontWindowRef, attributes,0,&attributeValues);
Line 110 I assign the AXUIElement (looping over the children array):
element = CFArrayGetValueAtIndex(childrenArrayRef, i);
Line 112 is where I add the AXUIElementRef to the array:
[_array addObject:(__bridge id)(element)];
Line 36 is where I empty it if it's over 500 elements:
if ([_array count] > 500) {
[_array removeAllObjects];
}
You can't just assume that __bridge_transfer "brings it over to ARC and [you] wouldn't need to manage any of that". You have to understand what rights and responsibilities you had for elementRef at the time (which we can't tell from what you've written).
I find it's much easier to understand the bridge casts if you use the corresponding CFBridgingRetain() and CFBridgingRelease() functions. __bridge_transfer is the same as CFBridgingRelease(). So, the question is: do you own elementRef at the time? Is it appropriate for you to release it?
Consider, would the following have been correct?
[_array addObject:(__bridge id)elementRef];
CFRelease(elementRef);
If not, then neither is __bridge_transfer.
From the nature of the error you're getting, I strongly suspect you were not entitled to release elementRef, so you were not entitled to tell ARC that it owns it and must release it. You have caused the AXUIElementRef to be over-released. You should just have used a __bridge cast – no "transfer" and no CF…Release().

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)

iPhone application error. NSString with whitespace

I am having a major problem with my iPhone app. My problem seems basic but I cannot find out the reason for the error. I have a NSString saveDescription that is set by user's input.
savede = civilsJobDescTb.text;
I then call in a NSLog to check the string has been set, and it is. So I have no problem there.
Later in the program I create a PDF file with several details. Everything works ok except the NSString that I have set earlier. It seem as if the string no longer exists. I get an error from it. EXC_BAD_ACCESS or something like that.
Now for the weird thing. This only happens when I have a whitespace in the text box. Eg: I enter "ok" and it will work without problem. But if I enter "Everything is ok" then I get errors.
I can't see where the problem is. Any ideas?
You didn't specify if your "savede" is an ivar (instance variable) but assuming that it is,
and if you are using ARC, set a strong property to it:
#property (nonatomic, strong) NSString * savede;
self.savede = civilsJobDescTb.text;
If you are not using ARC, then make sure to retain your "savede" string after setting it. Something like:
savede = civilsJobDescTb.text;
[savede retain];
(making sure to release it when you are truly finished with it).
You can also make a copy of the string (in case the label that it's coming from disappears, or is cleared, or somehow disappears).
[savede setString: civilsJobDescTb.text];
[savde retain];

Object released with CFRelease causes obvious crash, but only rarely

I have the following method:
+ (NSString*) getMD5HashFromFile:(NSString*)filePath {
CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
NSString *hashStr = (NSString*)md5hash;
CFRelease(md5hash);
return hashStr;
}
I was getting random crashes on the Simulator, about 1 in 20-30 executions. The fact that this wasn't consistent didn't help me dig deeper before.
Now that I see the code again, it seems obvious that md5hash gets released before being returned, which means the returned object is invalidated. The returned value is used in another method in a consistent way that crashes sometimes, but not always. My question is why this only happens rarely and not always.
Does it have something to do with the mix of Obj-C and C code and the way autorelease pools work?
Note: The bug seems to be fixed by using NSString *hashStr = [NSString stringWithString:(NSString*)md5hash], which makes total sense to me.
Just because a piece of memory is released and deallocated doesn't mean that it's immediately returned to the OS. Your application can hold onto it for an arbitrary period of time based on numerous factors and at several layers. The OS has more important things to do sometimes than reclaim every piece of memory you let go of and might ask for again in half a second. Accessing memory that you've called free() on, but technically own, does not generate a signal. This is why MallocScribble exists. It overwrites memory that you free with trash (0x55) so that it's more obvious when you use freed memory.
Try the following:
char *foo = malloc(100);
strcpy(foo, "stuff");
free(foo);
printf("%s", foo);
Most of the time that'll work fine, despite being completely wrong. Now, edit your Scheme>Diagnostics and Enable Scribble. Re-run and you'll see a bunch of "U" (0x55) indicating that you're reading nonsense. But it still won't crash.
You may be interested in Matt Gallagher's A look at how malloc works on the Mac for a bit more on the topic.
CFRelease argument must not be NULL.
If CFRelease argument is NULL, this will cause a runtime error and
your application will crash
if(md5hash)
CFRelease(md5hash);
+(NSString*) getMD5HashFromFile:(NSString*)filePath {
CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
NSString *hashStr = [(NSString*)md5hash copy];
CFRelease(md5hash);
return [hashStr autorelease];
}
make sure to retain the returned value in the caller if you need to hang on to it for any length of time.