Weird EXC_BAD_ACCESS in a trivial PDFKit program - objective-c

I have written this trivial action method associated to a textfield.
Every time I enter text into a textfield a search in a PDF is performed and PDFView automatically scroll to selection:
- (IBAction) search:(id)id
{
NSString *query = [self.searchView stringValue]; // get from textfield
selection = [document findString: query fromSelection:NULL withOptions:NSCaseInsensitiveSearch];
if (selection != nil)
{
[self.pdfView setCurrentSelection:selection];
[self.pdfView scrollSelectionToVisible:self.searchView];
}
}
Problem is that after 3 or 4 searches I get EXC_BAD_ACCESS on row (i).
If I debug I see that query contains an NSCFString and not an NSString.
I think it is a memory management problem..but where?
I replicated the same issue inside a trivial testcase:
#interface PDFRef_protoTests : SenTestCase {
#private
PDFDocument *document;
}
........
- (void)setUp
{
[super setUp];
document = [[PDFDocument alloc] initWithURL: #"a local url ..."];
}
- (void)test_exc_bad_access_in_pdfdocument
{
for (int i =0 ;i<100; i++)
{
NSString *temp;
if (i % 2 == 0) temp = #"home";
else if (i % 3 ==0) temp = #"cocoa";
else temp=#"apple";
PDFSelection *selection = [document findString: temp
fromSelection:nil
withOptions:NSCaseInsensitiveSearch];
NSLog(#"Find=%#, iteration=%d", selection, i);
}
}
Update:
1) It seems that it happens also if I use asynchronous search (method beginFindString: withOptions) every time I perform second search.
2) I found a similar problem to mine in MacRuby Issue Tracking: http://www.macruby.org/trac/ticket/1029
3) It seems that if I disable temporarily garbage collection it works but memory goes up.
I wrote something like:
[[NSGarbageCollector defaultCollector] disable];
[[NSGarbageCollector defaultCollector] enable];
surrounding search code
Another Update
Very weird thing is that sometimes all works. Than I clean and Rebuild and problem arises again. From a certain point of view is is not 100% reproducible. I suspect a bug in PDFKit or some compiler setting I have to do
Update Again
Dears it seems very crazy. I'd concentrate on testcase which is very trivial and which replicates easily the problem. What's wrong with it? This testcase works only if I disable (by code or by project setting) GC
Another Update
Boys it seems a bug but I downloaded an example called PDFLinker from Apple website (http://developer.apple.com/library/mac/#samplecode/PDFKitLinker2/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003594). This example implements a PDFViewer. Code of my app and this example are quite similars. For the same search action on same PDF, my memory rises at 300/400 MB while PDFLinker rises at 190MB. Clearly there is something wrong in my code. But I am comparing it bit by bit and I don't think I am inserting memory leaks (and Instrument doesn't give me any evidence). Maybe is there some project-wide setting ?
Update Yet
Changing from 64 bit to 32 bit memory consumption lowered. Surely there is a problem with 64bit and PDFKit. BTW still EXC_BAD_ACCESS on second search
SOLUTION
Crucial point is that PDFKit with Garbage collection is bugged.
If I disable GC all works correctly.
I had another issue that had complicated my analysis: I disabled GC on Project Setting but GC remained enabled on Target Settings. So Apple's example PDFLinked2 worked while mine not.

I agree you have found a bug in PDFKit.
I got various forms of errors (segmentation fault, selector not understood, etc) running your test case. Wrapping the code in #try/#catch doesn't prevent all errors associated with this method.
I also got errors printing the log message.
To work around the bug(s), I suggest you disable GC during your invocation of -findString:fromSelection:, as you've already discovered.
Also, be sure to make copies of the values of interest from selection before re-enabling GC. Don't just copy selection either.
If you conduct searches from multiple places in your code I also suggest you extract a separate method to perform the search. Then you can invoke that one to conduct the searches for you without duplicating the GC disable/enable nesting.

This sort of thing is usually evidence that you're hanging onto a pointer to an object that has been destroyed. Turn on zombie objects (with NSZombieEnabled) to see exactly where and when you're accessing a bad object.

Judging from your screen shot it doesn't seem like you have NSZombie turned on. Probably the reason why it doesn't help you. Here's how you turn it on:
How to enable NSZombie in Xcode?
The screenshot you provided was otherwise very useful, but you really need NSZombie to figure out this kind of errors. Well, unless it's obvious, which it isn't from the code you posted.
EDIT: I read the comment that you're using garbage collection. I'm an iOS developer, so I have very limited experience with garbage collection in Objective-C, but as far as I understand NSZombie doesn't work in a garbage collected environment.
I'm not sure it should be possible to get EXC_BAD_ACCESS in a garbage collected environment, unless you create your own pointer and try to call methods on it without having created an object and I don't see why you would do that.
I've heard that some frameworks doesn't work well with garbage collection, but I wouldn't think PDFKit was among them. Anyway, the solution might be to not use garbage collection. Perhaps you should file a bug report with Apple.

keep PDFSelection *selection as a member variable and pass it in fromSelection: instead of nil.
It is possible that PDFDocument keeps the returned PDFSelection instance to improve the performance.

Did you try retaining the searchview stringvalue object before using it?
As you say it happens when you type fast and it happens for async calls, it is possible that the object stringValue is pointing to is being released between the time your query object is pointing to it, and the time you use it int the search.
You could try something like this to see if the problem persists:
- (IBAction) search:(id)id
{
NSString *query = [[self.searchView stringValue] retain]; // get from textfield
selection = [document findString: query fromSelection:NULL withOptions:NSCaseInsensitiveSearch];
if (selection != nil)
{
[self.pdfView setCurrentSelection:selection];
[self.pdfView scrollSelectionToVisible:self.searchView];
}
[query release];
}
Of course there is also the possibility that document is relased. How do you declare it? is it a property with retain? Can it be released by the time you are searching?
EDIT:
I see that you posted the code with the second parameter as NULL, but in your screenshot, this value is nil.
The documentation says you should use NULL when you want to start the search from the beginning.
http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Reference/QuartzFramework/Classes/PDFDocument_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003873-RH2-DontLinkElementID_1
And as the compiler interprets nil and NULL differently, this could be leading to some weird behavior internally.

Related

Why am I getting NSFastEnumerationMutationHandler in code that doesn't modify source array/set?

In my crash logs for my latest app version, I got an NSFastEnumerationMutationHandler followed by a crash referencing HKAnchoredObjectQuery initWithType for my code, but I am not intentionally mutating the NSMutableSet sources over which I am fast enumerating.
Here's my code:
for(HKSource* source in sources){
NSPredicate *predicate = [HKQuery predicateForObjectsFromSources:[NSSet setWithObject:source]];
HKAnchoredObjectQuery *newQuery = [[HKAnchoredObjectQuery alloc] initWithType:quantityType predicate:predicate anchor:anchor limit:HKObjectQueryNoLimit completionHandler:^(HKAnchoredObjectQuery *query, NSArray *results, NSUInteger newAnchor, NSError *error) {
completion(results);
}];
[healthStore executeQuery:newQuery];
}
Any suggestions as to why I am triggering NSFastEnumerationMutationHandler? I am not explicitly touching sources nor have I made a copy of it...is there a way HealthKit could be modifying the source? Even if it were, I would think that modifying source shouldn't trigger this as I am not directly touching sources. Any trouble-shooting advice or error-spotting would be much appreciated.
Here's the exact text from the crash log:
Latest Exception Backtrace:
1. libobjc.A.dylib objc_exception_throw
2. CoreFoundation _NSFastEumerationMutationHandler
3. App name 0x1000d8000
4. App name 0x1000d8000
5. App name 0x1000d8000
6. HealthKit _79-[HKAnchoredObjectQuery initWithType:predicate:anchor:limit:completionHandler:]_block_invoke <---this must be referring to my code above, as it's the only call to initWithType inside a fast enumeration
7. HealthKit _81-[HKAnchoredObjectQuery deliverSampleObjects:deletedObjects:withAnchor:forQuery:]_block_invoke_2 <-- this is an internal HealthKit call. deliverSampleObjects is not a publicly listed method of the interface.
I have not had a crash in the sim or on my phone, so this is the only info I have to go on.
Some code that gets called by the block named completion() in your code snippet is iterating over another collection and is doing so while that array is being modified. Keep in mind that the completionHandler of HKAnchoredObjectQuery runs on a background thread, so your code may be performing unsafe concurrent operations on objects when completionHandler gets called.
It looks like this happens in a block somewhere in a method named
[HKAnchoredObjectQuery initWithType:predicate:anchor:limit:completionHandler:]
So my guess is that the block in question is the completion handler, and the completion handler gets called asynchronously - while someone else is iterating through the same array and maybe modifying it.
Unfortunately this error doesn't show who is causing the trouble, only who is finding the trouble. I'd set a breakpoint on your own callback, check if it is called from a fast enumeration, and try to figure out who else might be modifying the data. Good luck.

no Objective-C description available - Why?

I really have no idea why on this particular project my debugger is extremely 'disabled'.
For example I'll want to get info on an object:
(lldb) po [_model dictionaryValue]
[no Objective-C description available]
I'm wondering why this is. It's making debugging extremely difficult and it's only on this current project. I'm guessing I've done something to the settings at some point. It's on almost any po someObject I try to inspect. The variables in scope can be seen in the pane to the left of the debug console however.
I'm on Xcode 5, I have Cocoapods in my project, and it's a Unit Testing Target.
Any insights or any way to fix this?
Update:
For clarity, part of how the test case is implemented:
#interface WWCGateModelTests : XCTestCase
{
WWCGate *_model;
}
#end
#implementation WWCGateModelTests
- (void)setUp
{
[super setUp];
// Put setup code here; it will be run once, before each test case.
_model = [WWCGate loadGateModelWithIdentifier: kGateName]; // defined, not nil
}
- (void)tearDown
{
[super tearDown];
NSError *error = nil;
[_model saveModelOrError:&error];
// Breakpoint here. po _model does not print the model.
// This has been possible with other projects... po error will print
// nil to the console. How is an ivar not in scope?
}
This is likely happening because Unit Testing Targets typically are set up to run with "Release" configurations. "Release" configurations are ones where the debugging symbols have been stripped or optimized away.
I suspect you won't have this problem if you make certain you're running with a non-optimized, symbols-in-place Debug version of your app. You can change that in Xcode's scheme editor (e.g. when doing "Test" or "Profile", use the "Debug" configuration).
Are you sure you aren't using it on primitive types? Use p intVariable on those.
Every object will respond to description by at least printing class and memory address.
I've tracked down the issue (but haven't fixed it fully yet). It has to do with the Mantle Framework. In its description method it wants to spit out the contents of an NSDictionary that it generates at that point. There's something wrong with the way I set up my model I believe so creating this dictionary (based probably on how I configured some property) is basically failing.
I overrode the description method to return a standard description:
- (NSString*)description
{
return [NSString stringWithFormat:#"<%#: %p>", self.class, self];
}
and everything is fine and good again on planet Earth. ;-)
Thanks to those who were particularly patient. A more detailed discussion about this issue can be found at UPDATE 3 of the following post: http://horseshoe7.wordpress.com/2013/05/26/hands-on-with-the-mantle-model-framework/

How to save a setting into Core Data when a document is closed

I have a variable that gets changed a lot that I have therefore pulled out of my core data database (it's just an int but it gets updated very regularly).
My app is NSPersistentDocument based and when the user closes a document I would like to save this variable into the database so it persists.
I did the following:
- (void) windowWillClose:(NSNotification *)notification
{
[self.managedObjectContext performBlockAndWait:^(void) {
self.myDatabase.myNumber = [NSNumber numberWithInt:self.myTempInt];
[self.managedObjectContext save:nil];
}];
}
This works in terms of saving the data, and when a new document opens I can set myTempInt from the database value.
However, it has introduced a problem in that the document no longer deallocates – it seems that referencing self.myDatabase at the moment the document is closing creates a retain that means ARC never kicks in.
Is there a better place to do this kind of thing that won't be a problem for reference counting or am I approaching it all wrong?
Update:
I've been thinking this over and realised that it would be far better to do this operation whenever the document autosaves, rather than when it closes. Is there something like 'documentWillAutosave' I could use?

EXC_BAD_ACCESS on animationForKey:

I'm trying to use a recent feature of the Scintilla component, which provides OSX-like text-highlighting effect (the yellow animated bouncing box), and I'm stuck with an error that pops up intermittently :
EXC_BAD_ACCESS
pointing to this particular line :
if (layerFindIndicator!=nil)
if ([layerFindIndicator animationForKey:#"animateFound"])
[layerFindIndicator removeAnimationForKey:#"animateFound"];
(the ifs are mine; just in case I caught the object layerFindIndicator being nil, or deallocated or whatever... Unfortunately, it doesn't help...)
layerFindIndicator is seemingly a subclass of CAGradientLayer. (You may see the full code for layerFindIndicator, here).
Since, I'm an absolute newbie to Quartz Core, could please give me any hint as to HOW this could be debugged?
Since, I'm an absolute newbie to Quartz Core, could please give me any hint as to HOW this could be debugged?
This doesn't have anything to do with QuartzCore specifically (at least, I hope not)—it's general this-object-has-been-killed-before-its-time-how-do-I-find-the-killer stuff.
In Xcode:
Edit your current scheme.
For the Profile action, set it to use the Debug build configuration.
Dismiss that and then hit the Profile command.
Xcode will build for that action and then launch Instruments.
Instruments will prompt you to choose a template; you want the Zombies template. Once you've chosen it, Instruments will create a trace document and run your application. Switch to your application (if it isn't already frontmost), then do whatever causes the crash.
If the crash really is a dead-object crash, Zombies will reveal it. You'll get a flag in Instruments's timeline saying something like “message sent to zombie object 0xd3c2b1a0”, and your program will probably exit shortly thereafter.
In that flag is a tiny little button that looks like this: ➲ except it'll be gray. Click on it.
That takes you to the history of that object (actually of that address, including any previous objects or other allocations that have started at that address). Show your Extended Detail Pane (the one that appears on the right showing a stack trace), then scroll down to the end and then move backward (upward) step by step through time, looking at releases and autoreleases, looking for the one that isn't balancing out the object's allocation or a retain.
The solution will probably involve one or more of:
Changing a property to be strong or weak rather than assign/unsafe_unretained
Adding a property where you previously did not strongly own an object
Rearchitecting some things, if it's not clear which of the above you need to do or if either one of them seems like a filthy hack
Switching to ARC to get weak properties and __weak instance variables (both of which get set to nil automatically when the referenced object dies) and to get local variables being implicitly initialized to nil
But it'll depend on what you find in Instruments. And, of course, there's the chance that your problem—the bad access—isn't a dead object at all and all of the above will not help you.
Try this:
if (layerFindIndicator!=nil){
if ([layerFindIndicator animationForKey:#"animateFound"]){
[layerFindIndicator removeAnimationForKey:#"animateFound"];
}
}
Also check to see if it is released else were.
EDIT:
Another thing I found was you didn't have an white space in the if. Your code should now look like this:
if (layerFindIndicator != nil){
if ([layerFindIndicator animationForKey:#"animateFound"]){
[layerFindIndicator removeAnimationForKey:#"animateFound"];
}
}

Objective-c -> class method to main file query

Sorry to bug twice so quickly, but since people were so kind in their informative responces, I figured it couldnt hurt to ask another question.
The same program i tried to make it rather swanky and have a main screen which allows you to click on a button which leads to a limited options screen. This lets you switch the music on or off. Or at least it should do.
The music running code is in the main file (game.m), under the following:
//Music
[Settings setMusicEnabled:YES];
music = [SPSound soundWithContentsOfFile:#"music.caf"];
channel = [[music createChannel] retain];
channel.loop = YES;
channel.volume = 0.25;
if([Settings musicEnabled]){
[channel play];
}
I apologize for the strange format, but it is Sparrow framework. basically, the Settings file contains the class methods I am trying to use. If the methods cause YES, the music is on. If it is No, then the music is off.
settings.m
static BOOL isMusicEnabled;
#implementation Settings
+ (BOOL)musicEnabled
{
return isMusicEnabled;
}
+ (void)setMusicEnabled:(BOOL)value
{
isMusicEnabled = value;
NSLog(#"SME? %i", isMusicEnabled);
}
#end
Now, the options file is working and i tested that section. The program is reading that isMusicEnabled is getting a new value, thus musicEnabled is being altered as well, so there should be a change and the music should be switched off.
However, nothing happens. I have tried to use debugger, but I am not very good at it and I dont understand a lot of the information i am given. I do understand that the problem is sending the message from Settings file to the main/Game file.
I would appriciate anyone's help who could enlighten me as to how this could be solved.
I'm not familiar with Sparrow Framework, but let me make a guess anyway.
[channel play]; starts playing the music in background until the channel is asked to stop playing.
Changing the isMusicEnabled does not trigger any code to stop the currently playing music. When you change the value in Settings, you should inform the channel to stop (most probably by somehow accessing the channel and calling [channel stop].
There's another problem - isMusicEnabled is just a variable in memory, your program will not remember its state between restarts. And Settings are usually supposed to be remembered.
To summarize I see two problems: persisting settings between restarts first and informing about change of settings second. To remember settings I suggest you look into NSUserDefaults class. To inform the channel to stop playing you have couple of options - depending on you skills. Easiest is to simply access the channel variable from within the setMusicEnabled and call stop. Another option would be to use notifications, but for a beginner programmer that is more complicated (look for NSNotificationCenter if interested).