Is it possible to access arguments for caller and caller-of-caller via Obj-C runtime. I only found:
NSLog(#"%#", [NSThread callStackSymbols]);
I know it is possible, but is there any instruments (API) ?
UPDATE 1:
Maybe we can analyze [NSThread callStackSymbols] to determine number of arguments in each nested call. And then walk thru the stack ...
UPDATE 2:
I am going deeper, [NSThread callStackSymbols] is:
0 MyApp 0x00009ee5 -[HNPViewController mapView:didDeselectAnnotationView:] + 117,
1 MapKit 0x00404d12 -[MKMapView annotationContainer:didDeselectAnnotationView:] + 100,
2 MapKit 0x00411f90 -[MKAnnotationContainerView _deselectAnnotationViewWithAnimation:tellDelegate:] + 204,
3 MapKit 0x003fef87 -[MKMapView handleTap:] + 544,
4 UIKit 0x007c485a _UIGestureRecognizerSendActions + 139,
5 UIKit 0x007c399b -[UIGestureRecognizer _updateGestureWithEvent:] + 333,
6 UIKit 0x007c50df -[UIGestureRecognizer _delayedUpdateGesture] + 46,
7 UIKit 0x007c7d2d ___UIGestureRecognizerUpdate_block_invoke_0543 + 57,
8 UIKit 0x007c7cac _UIGestureRecognizerRemoveObjectsFromArrayAndApplyBlocks + 331,
9 UIKit 0x007bfa28 _UIGestureRecognizerUpdate + 1348,
10 CoreFoundation 0x01870afe __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30,
11 CoreFoundation 0x01870a3d __CFRunLoopDoObservers + 381,
12 CoreFoundation 0x0184e7c2 __CFRunLoopRun + 1106,
13 CoreFoundation 0x0184df44 CFRunLoopRunSpecific + 276,
14 CoreFoundation 0x0184de1b CFRunLoopRunInMode + 123,
15 GraphicsServices 0x029097e3 GSEventRunModal + 88,
16 GraphicsServices 0x02909668 GSEventRun + 104,
17 UIKit 0x004f9ffc UIApplicationMain + 1211,
18 MyApp 0x0000281d main + 141,
19 MyApp 0x00002745 start + 53
And I am printing (id)*(&mapView+i) where mapView is first argument and i = 0 .. N:
(id)*(&mapView+0) = 0x09e55670 <MKMapView: 0x9e55670; frame = (0 0; 320 460); clipsToBounds = YES; autoresize = TM+BM; layer = <CALayer: 0x9e55720>>
(id)*(&mapView+1) = 0x0046ec8f [no Objective-C description available]
(id)*(&mapView+2) = 0x09e4bd90 <HNPViewController: 0x9e4bd90>
(id)*(&mapView+3) = 0xe9a8b620 [no Objective-C description available]
(id)*(&mapView+4) = 0x00000020 [no Objective-C description available]
(id)*(&mapView+5) = 0x0046ec8f [no Objective-C description available]
(id)*(&mapView+6) = 0x00000054 [no Objective-C description available]
(id)*(&mapView+7) = 0xbfffda28 [no Objective-C description available]
(id)*(&mapView+8) = 0x00404d12 [no Objective-C description available]
(id)*(&mapView+9) = 0x09e4bd90 <HNPViewController: 0x9e4bd90>
(id)*(&mapView+10) = 0x0046ec8f [no Objective-C description available]
(id)*(&mapView+11) = 0x09e55670 <MKMapView: 0x9e55670; frame = (0 0; 320 460); clipsToBounds = YES; autoresize = TM+BM; layer = <CALayer: 0x9e55720>>
(id)*(&mapView+12) = 0x14346af0 <MKUserLocationView: 0x14346af0; frame = (1.34202e+06 3.24204e+06; 23 23); layer = <MKUserLocationLayer: 0x1433b8f0>> accuracy:-1.000000 +37.78583400, -122.40641700
(id)*(&mapView+13) = 0x14346af0 <MKUserLocationView: 0x14346af0; frame = (1.34202e+06 3.24204e+06; 23 23); layer = <MKUserLocationLayer: 0x1433b8f0>> accuracy:-1.000000 +37.78583400, -122.40641700
(id)*(&mapView+14) = 0x00aa61d8 [no Objective-C description available]
(id)*(&mapView+15) = 0x00000000 <nil>
(id)*(&mapView+16) = 0x00411ed2 [no Objective-C description available]
(id)*(&mapView+17) = 0x0aab0020 <MKAnnotationContainerView: 0xaab0020; frame = (0 0; 8.38861e+06 8.38861e+06); autoresizesSubviews = NO; layer = <CALayer: 0xaaaf810>>
(id)*(&mapView+18) = 0x14346af0 <MKUserLocationView: 0x14346af0; frame = (1.34202e+06 3.24204e+06; 23 23); layer = <MKUserLocationLayer: 0x1433b8f0>> accuracy:-1.000000 +37.78583400, -122.40641700
(id)*(&mapView+19) = 0xbfffda58 [no Objective-C description available]
[A] (id)*(&mapView+20) = 0x00411f90 [no Objective-C description available]
(id)*(&mapView+21) = 0x09e55670 <MKMapView: 0x9e55670; frame = (0 0; 320 460); clipsToBounds = YES; autoresize = TM+BM; layer = <CALayer: 0x9e55720>>
(id)*(&mapView+22) = 0x0046f3e3 [no Objective-C description available]
(id)*(&mapView+23) = 0x0aab0020 <MKAnnotationContainerView: 0xaab0020; frame = (0 0; 8.38861e+06 8.38861e+06); autoresizesSubviews = NO; layer = <CALayer: 0xaaaf810>>
(id)*(&mapView+24) = 0x14346af0 <MKUserLocationView: 0x14346af0; frame = (1.34202e+06 3.24204e+06; 23 23); layer = <MKUserLocationLayer: 0x1433b8f0>> accuracy:-1.000000 +37.78583400, -122.40641700
(id)*(&mapView+25) = 0x0aab0020 <MKAnnotationContainerView: 0xaab0020; frame = (0 0; 8.38861e+06 8.38861e+06); autoresizesSubviews = NO; layer = <CALayer: 0xaaaf810>>
(id)*(&mapView+26) = 0x00000054 [no Objective-C description available]
(id)*(&mapView+27) = 0x00000054 [no Objective-C description available]
(id)*(&mapView+28) = 0x003fed75 [no Objective-C description available]
(id)*(&mapView+29) = 0x09e55670 <MKMapView: 0x9e55670; frame = (0 0; 320 460); clipsToBounds = YES; autoresize = TM+BM; layer = <CALayer: 0x9e55720>>
(id)*(&mapView+30) = 0x49a3d250 [no Objective-C description available]
(id)*(&mapView+31) = 0xbfffdab8 [no Objective-C description available]
[B] (id)*(&mapView+32) = 0x003fef87 [no Objective-C description available]
(id)*(&mapView+33) = 0x0aab0020 <MKAnnotationContainerView: 0xaab0020; frame = (0 0; 8.38861e+06 8.38861e+06); autoresizesSubviews = NO; layer = <CALayer: 0xaaaf810>>
(id)*(&mapView+34) = 0x0046ddfb [no Objective-C description available]
(id)*(&mapView+35) = 0x00000001 [no Objective-C description available]
(id)*(&mapView+36) = 0x00000001 [no Objective-C description available]
(id)*(&mapView+37) = 0x00000001 [no Objective-C description available]
(id)*(&mapView+38) = 0x433f8000 [no Objective-C description available]
(id)*(&mapView+39) = 0xbfffdab8 [no Objective-C description available]
(id)*(&mapView+40) = 0x015c85b4 [no Objective-C description available]
(id)*(&mapView+41) = 0xbfffdaa0 [no Objective-C description available]
(id)*(&mapView+42) = 0x49a3d250 [no Objective-C description available]
(id)*(&mapView+43) = 0x4a45e070 [no Objective-C description available]
(id)*(&mapView+44) = 0x00000000 <nil>
(id)*(&mapView+45) = 0x0000000c [no Objective-C description available]
(id)*(&mapView+46) = 0x00000054 [no Objective-C description available]
(id)*(&mapView+47) = 0x41300000 [no Objective-C description available]
(id)*(&mapView+48) = 0x43dc0000 [no Objective-C description available]
(id)*(&mapView+49) = 0x41c00000 [no Objective-C description available]
(id)*(&mapView+50) = 0x41300000 [no Objective-C description available]
(id)*(&mapView+51) = 0x00000000 <nil>
(id)*(&mapView+52) = 0x007c47dd [no Objective-C description available]
(id)*(&mapView+53) = 0x0aaab070 <UITapGestureRecognizer: 0xaaab070; state = Ended; delaysTouchesEnded = NO; view = <UIView 0xaa93f00>; target= <(action=handleTap:, target=<MKMapView 0x9e55670>)>; must-fail = {
<MKVariableDelayTapRecognizer: 0xaaad260; baseClass = UITapGestureRecognizer; state = Failed; view = <UIView 0xaa93f00>; target= <(action=handleDoubleTap:, target=<MKMapGestureController 0xaaadbe0>)>; numberOfTapsRequired = 2>
}>
(id)*(&mapView+54) = 0x00000004 [no Objective-C description available]
(id)*(&mapView+55) = 0xbfffdb08 [no Objective-C description available]
[C] (id)*(&mapView+56) = 0x007c485a [no Objective-C description available]
(id)*(&mapView+57) = 0x09e55670 <MKMapView: 0x9e55670; frame = (0 0; 320 460); clipsToBounds = YES; autoresize = TM+BM; layer = <CALayer: 0x9e55720>>
(id)*(&mapView+58) = 0x0046db75 [no Objective-C description available]
(id)*(&mapView+59) = 0x132e7ce0 [no Objective-C description available]
(id)*(&mapView+60) = 0x007c47dd [no Objective-C description available]
I added markers [A], [B] and [C] in rows where address is equal to address in callstack...
UPDATE 3:
Using this code I can determine if user tapped on my callout or not. I know I should not use this code, but it is just for experiment :)
if ([(id)*(&mapView+53) isKindOfClass:[UIGestureRecognizer class]])
{
CGPoint point = [(id)*(&mapView+53) locationInView:callout];
if (CGRectContainsPoint(callout.bounds, point))
{
NSLog(#"Inside the callout");
}
else
{
NSLog(#"Outside the callout");
}
}
// I have a problem not related to this question
// with callout right not, it closes after tap on it...
Related
I am populating an NSComboBox with some data from the function below. After it is populated and I try to scroll through the items I get the CATransaction warning. Can anyone shed some light on why this is happening and what I can do to fix it? I have figured out that it may have something to do with changing the UI of the combobox on a thread other than the main thread but after that I am stuck.
func getAllRecords()
{
CATransaction.begin()
let url = NSURL(string: "http://URL.php")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!)
{
(data, response, error) in
var d = NSString(data: data, encoding: NSUTF8StringEncoding)
var arr = d!.componentsSeparatedByString("<") // spliting the incoming string from "<" operator because before that operator is our required data and storing in array
var dataWeNeed:NSString = arr[0] as! NSString // arr[0] is the data before "<" operator and arr[1] is actually no use for us
if let data = NSJSONSerialization.JSONObjectWithData(dataWeNeed.dataUsingEncoding(NSUTF8StringEncoding)!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSArray
{
for dd in data
{
var name : String = dd["Name"]! as! String
var email : String = dd["Email"]! as! String
//println("Name: \(name)")
//println("Email: \(email)")
self.userComboBox.addItemsWithObjectValues([name])
}
}
}
task.resume()
CATransaction.commit()
}
Here is the warning I am getting from the debug area.
2015-06-14 13:54:04.756 Green Time Clock[2150:738395] Unexpected outstanding background CATransaction
CoreAnimation: warning, encountered thread with uncommitted CATransaction; created by:
0 QuartzCore 0x00007fff9ab4d6c2 _ZN2CA11Transaction4pushEv + 312
1 QuartzCore 0x00007fff9ab689a8 _ZN2CA11Transaction15ensure_implicitEv + 276
2 QuartzCore 0x00007fff9ab4d842 _ZN2CA11Transaction9set_valueEj12_CAValueTypePKv + 40
3 QuartzCore 0x00007fff9ab4f452 +[CATransaction setDisableActions:] + 38
4 AppKit 0x00007fff921a1b8c -[NSView(NSInternal) _updateLayerGeometryFromView] + 389
5 AppKit 0x00007fff921c7d09 -[NSView setFrameSize:] + 1129
6 AppKit 0x00007fff921c789a -[NSControl setFrameSize:] + 77
7 AppKit 0x00007fff922e3891 -[NSTableView setFrameSize:] + 256
8 AppKit 0x00007fff922e35f9 -[NSTableView _tileAndRedisplayAll] + 180
9 Green Time Clock 0x00000001000042d6 _TFFC16Green_Time_Clock14ViewController13getAllRecordsFS0_FT_T_U_FTGSQCSo6NSData_GSQCSo13NSURLResponse_GSQCSo7NSError__T_ + 2886
10 Green Time Clock 0x0000000100004463 _TTRXFo_oGSQCSo6NSData_oGSQCSo13NSURLResponse_oGSQCSo7NSError__dT__XFo_iTGSQS__GSQS0__GSQS1____iT__ + 51
11 Green Time Clock 0x0000000100001e31 _TPA__TTRXFo_oGSQCSo6NSData_oGSQCSo13NSURLResponse_oGSQCSo7NSError__dT__XFo_iTGSQS__GSQS0__GSQS1____iT__ + 81
12 Green Time Clock 0x0000000100004493 _TTRXFo_iTGSQCSo6NSData_GSQCSo13NSURLResponse_GSQCSo7NSError___iT__XFo_oGSQS__oGSQS0__oGSQS1___dT__ + 35
13 Green Time Clock 0x00000001000044fa _TTRXFo_oGSQCSo6NSData_oGSQCSo13NSURLResponse_oGSQCSo7NSError__dT__XFdCb_dGSQS__dGSQS0__dGSQS1___dT__ + 90
14 CFNetwork 0x00007fff8c09cba2 __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 157
15 Foundation 0x00007fff9a75f7e8 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
Not sure what root issue is, I found that warning when updating string value to a NSTextView object and resolved it with following code just now.
Just make sure the updating process is handled in main thread.
// Link: NSTextStorage limitation on size and frequency of updates
dispatch_block_t block = ^ {
NSAttributedString *attributeString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%#\n", strLog]];
NSTextStorage* store = [_textViewOutputLog textStorage];
[store beginEditing];
[store appendAttributedString:attributeString];
[store endEditing];
[_textViewOutputLog scrollRangeToVisible:NSMakeRange([[_textViewOutputLog string] length], 0)];
};
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
When I attempt:
import Cocoa
var ns: NSString = "whatever"
var cs: CString = ns.UTF8String
println(cs)
in an Playground it reports this exception when executing the var cs: CString = ns.UTF8String statement:
fatal error: Can't unwrap Optional.None
Playground execution failed: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
* thread #1: tid = 0xbf05a5, 0x000000010401b02d libswift_stdlib_core.dylib`Swift._StringCore._growBuffer (#inout Swift._StringCore)(Swift.Int, minElementWidth : Swift.Int) -> Swift.COpaquePointer + 813, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
* frame #0: 0x000000010401b02d libswift_stdlib_core.dylib`Swift._StringCore._growBuffer (#inout Swift._StringCore)(Swift.Int, minElementWidth : Swift.Int) -> Swift.COpaquePointer + 813
frame #1: 0x000000010401643f libswift_stdlib_core.dylib`Swift._StringCore.append (#inout Swift._StringCore)(Swift._StringCore) -> () + 607
frame #2: 0x0000000104016fa5 libswift_stdlib_core.dylib`Swift.+ #infix (Swift.String, Swift.String) -> Swift.String + 117
...truncated...
However the same code works as expected when compiled into an app. It looks like a Playground bug but does anyone know a workaround to convert an NSString to Cstring in the Playground?
I was able to get it to work in the playground by making cs a CString optional.
import Cocoa
var ns: NSString = "whatever"
println(ns)
var cs: CString? = ns.UTF8String
println(cs)
and if you need to unwrap the optional
if let str = cs{
println(str)
}
I have been facing this issue and I admit that I lack of some fundamental concepts of memory managements. I've not been able to solve this and trust me, I've been trying so many things out.
In my app, there are 4 threads (can be up to 12 threads in future) which will read from RS232 ports. In each thread (threadRS232Read), I'll append the RS232 characters into respective NSMutableString using appendString. the NSMutableString will grow incredibly large with the appendString until a full test is completed. And periodically inside a test, clearRdBuffStr is called to clear the string. The app always crashes in appendString. If I get lucky, I can run few tests but normally, it crashes on the first run. Below are the code snippets and the crash log.
AppController.h
...
`#interface AppController : NSObject {
...
NSMutableString *buffStr1, *buffStr2, *buffStr3, *buffStr4;
...}
AppController.m
...
//in -(id)init
buffStr1 = [[NSMutableString alloc] initWithString:#""];
buffStr2 = [[NSMutableString alloc] initWithString:#""];
buffStr3 = [[NSMutableString alloc] initWithString:#""];
buffStr4 = [[NSMutableString alloc] initWithString:#""];
...
// in -(void)dealloc
[buffStr1 release];
[buffStr2 release];
[buffStr3 release];
[buffStr4 release];`
In another file RS232RW.m, a thread will be used to update buffStr1 to 4
RS232RW.m
- (void)threadRS232Read:(id)argument {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
....
//read from RS232 port buffer and etc...
NSString *buffStr = [NSString stringWithUTF8String:buff];
switch (portNo) {
case 0:
if (buffStr != nil)
{
[buffStr1 appendString:buffStr];
}
break;
case 1:
if (buffStr != nil)
{
[buffStr2 appendString:buffStr];
}
break;
case 2:
if (buffStr != nil)
{
[buffStr3 appendString:buffStr];
}
break;
case 3:
if (buffStr != nil)
{
[buffStr4 appendString:buffStr];
}
break;
case 4:
if (buffStr != nil)
{
[buffStr5 appendString:buffStr];
}
break;
....
// clearRdBuffStr will be called by the other part of the program to clear this buffer.
-(void) clearRdBuffStr:(int) portNo {
switch (portNo) {
case 0:
[buffStr1 setString:#""];
break;
case 1:
[buffStr2 setString:#""];
break;
case 2:
[buffStr3 setString:#""];
break;
case 3:
[buffStr4 setString:#""];
break;
....
The app always crashes at 1 of the appendString above.
The crash log is as below:
....
Crashed Thread: 3
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Application Specific Information:
objc[584]: garbage collection is OFF
*** error for object 0x6d02b600: double free
....
Thread 3 Crashed:
0 libsystem_kernel.dylib 0x905819c6 __pthread_kill + 10
1 libsystem_c.dylib 0x926ecf78 pthread_kill + 106
2 libsystem_c.dylib 0x926ddbdd abort + 167
3 libsystem_c.dylib 0x92701508 szone_error + 333
4 libsystem_c.dylib 0x92702dd1 free_small_botch + 102
5 com.apple.CoreFoundation 0x97ee51e8 __CFAllocatorSystemDeallocate + 24
6 com.apple.CoreFoundation 0x97ee51ba CFAllocatorDeallocate + 266
7 com.apple.CoreFoundation 0x97ee50a2 __CFStrDeallocateMutableContents + 178
8 com.apple.CoreFoundation 0x97ee422b __CFStringChangeSizeMultiple + 3147
9 com.apple.CoreFoundation 0x97f86010 __CFStringCheckAndReplace + 496
10 com.apple.CoreFoundation 0x97f95dad -[__NSCFString appendString:] + 45
11 com.TopTestDFU 0x00109f6d -[AppController(RS232RW)
threadRS232Read:] + 752
12 com.apple.Foundation 0x92aabf7d -[NSThread main] + 45
13 com.apple.Foundation 0x92aabf2d __NSThread__main__ + 1582
14 libsystem_c.dylib 0x926eaed9 _pthread_start + 335
15 libsystem_c.dylib 0x926ee6de thread_start + 34
The Cocoa mutable objects such as NSMutableString are not thread safe. It's up to you to arrange synchronisation using some form of lock.
Check out NSLock for one option.
You might try making the 4 NSMutableStrings into atomic properties rather than ivars. It might work in this case, although atomic doesn't actually guarantee thread safety.
[edit]
... actually, if in your case, you are sure that each thread is using a different port number, then it's probably not the NSMutableStrings as such that are the problem. It's probably NSString *buffStr = [NSString stringWithUTF8String:buff]; is being called by one thread before another has finished appending buffStr. So you need a lock around the that as well as the append.
I have a Cocoa app that is intended to capture still images from a USB microscope and then do some post-processing on them before saving them to an image file. At the moment, I am stuck trying to get from the CMSampleBufferRef that's passed to my completionHandler block to an NSImage or some other representation I can manipulate and save using familiar Cocoa APIs.
I found the function imageFromSampleBuffer() in the AVFoundation docs, which purports to convert a CMSampleBuffer to a UIImage (sigh), and revised it appropriately to return an NSImage. But it does not work in this case, as the call to CMSampleBufferGetImageBuffer() returns nil.
Here is a log showing the CMSampleBuffer passed to my completion block:
2012-01-21 19:38:36.293 LabCam[1402:cb0f] CMSampleBuffer 0x100335390 retainCount: 1 allocator: 0x7fff8c78620c
invalid = NO
dataReady = YES
makeDataReadyCallback = 0x0
makeDataReadyRefcon = 0x0
buffer-level attachments:
com.apple.cmio.buffer_attachment.discontinuity_flags(P) = 0
com.apple.cmio.buffer_attachment.hosttime(P) = 79631546824089
com.apple.cmio.buffer_attachment.sequence_number(P) = 42
formatDescription = <CMVideoFormatDescription 0x100335220 [0x7fff782fff40]> {
mediaType:'vide'
mediaSubType:'jpeg'
mediaSpecific: {
codecType: 'jpeg' dimensions: 640 x 480
}
extensions: {<CFBasicHash 0x100335160 [0x7fff782fff40]>{type = immutable dict, count = 5,
entries =>
1 : <CFString 0x7fff773dff48 [0x7fff782fff40]>{contents = "Version"} = <CFNumber 0x183 [0x7fff782fff40]>{value = +1, type = kCFNumberSInt32Type}
2 : <CFString 0x7fff773dff68 [0x7fff782fff40]>{contents = "RevisionLevel"} = <CFNumber 0x183 [0x7fff782fff40]>{value = +1, type = kCFNumberSInt32Type}
3 : <CFString 0x7fff7781ab08 [0x7fff782fff40]>{contents = "CVFieldCount"} = <CFNumber 0x183 [0x7fff782fff40]>{value = +1, type = kCFNumberSInt32Type}
4 : <CFString 0x7fff773dfdc8 [0x7fff782fff40]>{contents = "FormatName"} = <CFString 0x7fff76d35fb0 [0x7fff782fff40]>{contents = Photo - JPEG"}
5 : <CFString 0x7fff773dff88 [0x7fff782fff40]>{contents = "Vendor"} = <CFString 0x7fff773dffa8 [0x7fff782fff40]>{contents = "appl"}
}
}
}
sbufToTrackReadiness = 0x0
numSamples = 1
sampleTimingArray[1] = {
{PTS = {2388943236/30000 = 79631.441, rounded}, DTS = {INVALID}, duration = {3698/30000 = 0.123}},
}
sampleSizeArray[1] = {
sampleSize = 55911,
}
dataBuffer = 0x100335300
It clearly appears to contain JPEG data, but how do I get at it? (Preferably keeping the associated metadata along for the rideā¦)
I eventually solved this with help from another code example. CMSampleBufferGetImageBuffer only returns a valid result for the uncompressed, native image formats available from the camera. So to get my program to work, I had to configure the AVCaptureStillImageOutput instance to use k32BGRAPixelFormat instead of its default (JPEG) compressed format.
session = [[AVCaptureSession alloc] init];
session.sessionPreset = AVCaptureSessionPresetPhoto;
imageOutput = [[AVCaptureStillImageOutput alloc] init];
// Configure imageOutput for BGRA pixel format [#2].
NSNumber * pixelFormat = [NSNumber numberWithInt:k32BGRAPixelFormat];
[imageOutput setOutputSettings:[NSDictionary dictionaryWithObject:pixelFormat
forKey:(id)kCVPixelBufferPixelFormatTypeKey]];
[session addOutput:imageOutput];
Hey guys... I'm at my wits end here. I've been focusing on this issue for the last 3 days it seems, and I'm still no closer to solving it. I've got a queue of videos that I'm converting one after the other in a background thread. Most of the time it works as expected, but every so often, I get a weird crash, always at the same point. I can't for the life of me figure out why it's happening. I've got garbage collection enabled. Here is my conversion code.
Here is the stack trace.
Edit: After a bit more debugging, I am back to the conclusion that maybe it is garbage collector related. If I place the following line just before the line that converts the video, I get a drastic increase in the amount of these errors that I see...
[[NSGarbageCollector defaultCollector] collectExhaustively];
NSInvalidArgumentException
-[NSPathStore2 objectForKey:]: unrecognized selector sent to instance 0x1073570
(
0 CoreFoundation 0x92fc16ba __raiseError + 410
1 libobjc.A.dylib 0x901b4509 objc_exception_throw + 56
2 CoreFoundation 0x9300e90b -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
3 CoreFoundation 0x92f67c36 ___forwarding___ + 950
4 CoreFoundation 0x92f67802 _CF_forwarding_prep_0 + 50
5 QTKit 0x903d3280 MovieProgressProc + 62
6 QuickTime 0x95a66062 convertFileProgress + 212
7 QuickTime3GPP 0x1e7bcaa2 Spit3GP2_Progress + 180
8 QuickTime3GPP 0x1e7c01d8 Spit3GP2_FromProceduresToDataRef + 3438
9 CarbonCore 0x90b0d054 _ZL38CallComponentFunctionCommonWithStoragePPcP19ComponentParametersPFlvEm + 54
10 QuickTime3GPP 0x1e7be33d Spit3GP2_ComponentDispatch + 129
11 CarbonCore 0x90b057c9 CallComponentDispatch + 29
12 QuickTime 0x95befb97 MovieExportFromProceduresToDataRef + 49
13 QuickTime3GPP 0x1e7bdf84 Spit3GP2_ToDataRef + 1987
14 CarbonCore 0x90b1865d callComponentStorage_4444444 + 63
15 CarbonCore 0x90b0d054 _ZL38CallComponentFunctionCommonWithStoragePPcP19ComponentParametersPFlvEm + 54
16 QuickTime3GPP 0x1e7be33d Spit3GP2_ComponentDispatch + 129
17 CarbonCore 0x90b057c9 CallComponentDispatch + 29
18 QuickTime 0x95befbe2 MovieExportToDataRef + 73
19 QuickTime 0x95a6e9bb ConvertMovieToDataRef_priv + 1690
20 QuickTime 0x95bdc591 ConvertMovieToDataRef + 71
21 QTKit 0x903e0954 -[QTMovie_QuickTime writeToDataReference:withAttributes:error:] + 2692
22 QTKit 0x903c5110 -[QTMovie_QuickTime writeToFile:withAttributes:error:] + 111
23 Mevee 0x0005871d -[ConversionQueue convertVideo:] + 509
24 Mevee 0x00058341 -[ConversionQueue startConvertingItems] + 145
25 Foundation 0x9520fbf0 -[NSThread main] + 45
26 Foundation 0x9520fba0 __NSThread__main__ + 1499
27 libSystem.B.dylib 0x9475a85d _pthread_start + 345
28 libSystem.B.dylib 0x9475a6e2 thread_start + 34
)
- (id) init
{
if(!(self = [super init])) return self;
convertingIndex = 0;
conversionPaths = [[NSMutableArray alloc] init];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/2 Fast 2 Furious/2 Fast 2 Furious.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/101 Dalmations/101 Dalmations.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/300/300.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/1408/1408.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/A Few Good Men/A Few Good Men.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/A Goofy Movie/A Goofy Movie.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/A Single Man/A Single Man.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/A View to a Kill/A View to a Kill.mp4"];
[conversionPaths addObject:#"/Users/Morgan/Desktop/Convertable Media/Movies/Across the Universe/Across the Universe.mp4"];
backgroundThread = [[NSThread alloc] initWithTarget:self selector:#selector(startConvertingItems) object:nil];
[backgroundThread start];
return self;
}
- (void) startProcessingQueue
{
}
- (void) startConvertingItems
{
NSInteger iterations = 0;
while(iterations < 100)
{
NSString* nextPath = [conversionPaths objectAtIndex:convertingIndex];
NSLog(#"ITERATION %d", iterations);
[self convertVideo:nextPath];
convertingIndex += 1;
if(convertingIndex >= [conversionPaths count])
convertingIndex = 0;
iterations += 1;
}
}
- (void) openMovieOnMainThread:(NSString*)path
{
NSError* error = nil;
movie = [[QTMovie alloc] initWithFile:path error:&error];
if(movie == nil || error != nil || ![movie detachFromCurrentThread])
movie = nil;
}
- (void) closeMovieOnMainThread
{
//[movie attachToCurrentThread];
//[movie release];
}
- (BOOL) convertVideo: (NSString*)path
{
[self performSelectorOnMainThread:#selector(openMovieOnMainThread:) withObject:path waitUntilDone:YES];
if(movie == nil) {
NSLog(#"ERROR OPENING MOVIE");
return NO;
}
[QTMovie enterQTKitOnThreadDisablingThreadSafetyProtection];
[movie attachToCurrentThread];
[movie setDelegate:self];
NSString* tempItemPath = #"/Users/Morgan/Desktop/test.mp4";
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], QTMovieExport,
[NSNumber numberWithLong:'3gpp'], QTMovieExportType,
nil];
NSError* error = nil;
if(![movie writeToFile:tempItemPath withAttributes:attrs error:&error]) {
NSLog(#"ERROR CONVERTING MOVIE");
return NO;
}
[movie invalidate];
[movie detachFromCurrentThread];
[QTMovie exitQTKitOnThread];
[self performSelectorOnMainThread:#selector(closeMovieOnMainThread) withObject:nil waitUntilDone:YES];
return YES;
}
- (BOOL)movie:(QTMovie *)aMovie shouldContinueOperation:(NSString *)op withPhase:(QTMovieOperationPhase)phase atPercent:(NSNumber *)percent withAttributes:(NSDictionary *)attributes
{
switch (phase)
{
case QTMovieOperationBeginPhase:
NSLog(#"Conversion started");
break;
case QTMovieOperationUpdatePercentPhase:
NSLog(#"Conversion progress: %f", [percent floatValue]);
break;
case QTMovieOperationEndPhase:
NSLog(#"Conversion finished.");
break;
}
return YES;
}
Ahhh... I found out it was the stupid garbage collector. Reworked my app to work with reference counting rather than garbage collection, and smooth sailing. Has anyone else come across similar garbage collection bugs? I had a strong reference to a root object for my movie file, so I don't think that was the problem.