Cocoa retain single object from array - xcode

In Cocoa, I get the array of windows of an app using the following code:
CFArrayRef windows;
AXError gettingWindowsResult = AXUIElementCopyAttributeValues(app, (CFStringRef)NSAccessibilityWindowsAttribute, 0, 999, &windows);
Then I check some values of those windows and keep the AXUIElementRef of one of them in a variable of my class. At the end of the method, I release the CFArrayRef to make sure I don't have any memory leaks:
if (windows != nil)
{
CFRelease(windows);
}
Though this makes it so that when I try to use the window I kept, I get a bad access error. So my question: is it necessary that I release the array? And if so, how do I prevent the bad access error?

Why not make a retained copy of the "AXUIElementRef" of the one element you want to keep?
To do this, figure out the index of the element you want to keep, and then make another call to the "AXUIElementCopyAttributeValues" function, only this time just pass the index for the element you desire and a "maxValue" of 1. For example, for the element at position 26:
AXError gettingWindowsResult = AXUIElementCopyAttributeValues(app, (CFStringRef)NSAccessibilityWindowsAttribute, 26, 1, &arrayOfOne);
Then you can safely call "CFRelease" on the "windows" array.

Related

Subclassing NSTextStorage breaks list editing

I have a basic Mac app with a standard NSTextView. I'm trying to implement and use a subclass of NSTextStorage, but even a very basic implementation breaks list editing behavior:
I add a bulleted list with two items
I copy & paste that list further down into the document
Pressing Enter in the pasted list breaks formatting for the last list item.
Here's a quick video:
Two issues:
The bullet points of the pasted list use a smaller font size
Pressing Enter after the second list item breaks the third item
This works fine when I don't replace the text storage.
Here's my code:
ViewController.swift
#IBOutlet var textView:NSTextView!
override func viewDidLoad() {
[...]
textView.layoutManager?.replaceTextStorage(TestTextStorage())
}
TestTextStorage.swift
class TestTextStorage: NSTextStorage {
let backingStore = NSMutableAttributedString()
override var string: String {
return backingStore.string
}
override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key:Any] {
return backingStore.attributes(at: location, effectiveRange: range)
}
override func replaceCharacters(in range: NSRange, with str: String) {
beginEditing()
backingStore.replaceCharacters(in: range, with:str)
edited(.editedCharacters, range: range,
changeInLength: (str as NSString).length - range.length)
endEditing()
}
override func setAttributes(_ attrs: [NSAttributedString.Key: Any]?, range: NSRange) {
beginEditing()
backingStore.setAttributes(attrs, range: range)
edited(.editedAttributes, range: range, changeInLength: 0)
endEditing()
}
}
You have found a bug in Swift (and maybe not just in the Swift libraries, maybe in something a bit more fundamental).
So what is going on?
You will be able to see this a bit better if you create a numbered list rather than a bulleted one. You don't need to do any copy and paste, just:
Type "aa", hit return, type "bb"
Do select all and format as a numbered list
Place cursor at the end of "aa" and hit return...
What you see is a mess, but you can see the two original numbers are still there and the new middle list item you started by hitting return is where all the mess is.
When you hit return the text system has to renumber the list items, as you've just inserted a new item. First, it turns out that it performs this "renumbering" even if it is a bulleted list, which is why you see the mess in your example. Second, it does this renumbering by starting at the beginning of the list and renumbering every list item and inserting a new number for the just created item.
The Process in Objective-C
If you translate your Swift code into the equivalent Objective-C and trace through you can watch the process. Starting with:
1) aa
2) bb
the internal buffer is something like:
\t1)\taa\n\t2)\tbb
first the return is inserted:
\t1)\taa\n\n\t2)\tbb
and then an internal routine _reformListAtIndex: is called and it starts "renumbering". First it replaces \t1)\t with \t1) - the number hasn't changed. Then it inserts \t2)\t between the two new lines, as at this point we have:
\t1)\taa\n\t2)\t\n\t2)\tbb
and then it replaces the original \t2)\t with \t3)\t giving:
\t1)\taa\n\t2)\t\n\t3)\tbb
and it's job is done. All these replacements are based on specifying the range of characters to replace, the insertion uses a range of length 0, and go through:
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString * _Nonnull)str
which in Swift is replaced by:
override func replaceCharacters(in range: NSRange, with str: String)
The Process in Swift
In Objective-C strings have reference semantics, change a string and all parts of the code with a reference to the string see the change. In Swift strings have value semantics and strings are copied (notionally at least) on being passed to functions etc.; if the copy is changed in called function the caller won't see that change in its copy.
The text system was written in (or for) Objective-C and it is reasonable to assume it may take advantage of the reference semantics. When you replace part of its code with Swift the Swift code has to do a little dance, during the list renumbering stage when replaceCharacters() gets called the stack will look something like:
#0 0x0000000100003470 in SwiftTextStorage.replaceCharacters(in:with:)
#1 0x0000000100003a00 in #objc SwiftTextStorage.replaceCharacters(in:with:) ()
#2 0x00007fff2cdc30c7 in -[NSMutableAttributedString replaceCharactersInRange:withAttributedString:] ()
#3 0x00007fff28998c41 in -[NSTextView(NSKeyBindingCommands) _reformListAtIndex:] ()
#4 0x00007fff284fd555 in -[NSTextView(NSKeyBindingCommands) insertNewline:] ()
Frame #4 is the Objective-C code called when return was hit, after inserting the newline it calls the internal routine _reformListAtIndex:, frame #3, to do the renumbering. This calls another Objective-C routine in frame #2, which in turn calls, frame #1, what it thinks is the Objective-C method replaceCharactersInRange:withString:, but is in fact a Swift replacement. This replacement does a little dance converting Objective-C reference semantic strings to Swift value semantics strings and then calls, frame #0, the Swift replaceCharacters().
Dancing is Hard
If you trace through your Swift code just as you did the Objective-C translation when the renumbering gets to the stage of changing the original \t2)\t to \t3)\t you will see a misstep, the range given for the original \t2)\t is what is was before the new \t2)\t was inserted in the previous step (i.e. it is off by 4 positions)... and you end up with a mess and a few more dance steps later the code crashes with a string referring error as the indices are all wrong.
This suggests that the Objective-C code is relying on reference semantics, and the choreographer of the Swift dance converting reference to value and back to reference semantics has failed to meet the Objective-C code's expectations: so either when the Objective-C code, or some Swift code which has replaced it, calculates the range of the original \t2)\t it is doing so on string which hasn't been altered by the previous insertion of the new \t2)\t.
Confused? Well dancing can make you dizzy at times ;-)
Fix?
Code your subclass of NSTextStorage in Objective-C and go to bugreport.apple.com and report the bug.
HTH (more than it makes you dizzy)

Is there are non-deprecated way to access font collections for OS X 10.11?

I was looking at adding an option for sorting available fonts by user-defined font collections (I wish Pages and Keynote did this!), but it looks like the old ways of accessing these collections are being deprecated in 10.11:
https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSFontManager_Class/index.html#//apple_ref/occ/clm/NSFontManager/
Is there a new way of accessing and using those font collections?
I've recently been working with font collections, so I may have some info for you.
The NSFontCollection API is kind of strange. It offers access to "Named Font Collections," but the names associated with said collections aren't attached to them. If that makes no sense, it's because it doesn't. However, let me try to break it down:
To add a system-wide font collection:
// Create a font descriptor (or descriptors) with whatever attributes you want
let descriptor = NSFontDescriptor(fontAttributes: nil)
// Create a font collection with the above descriptor(s)
let collection = NSFontCollection(descriptors: [descriptor])
// In Objective-C, the `+ showFontCollection:withName:visibility:error:`
// method returns `YES` if the collection was shown or `NO` if an error occurred.
// The error is passed via pointer supplied to the `error:` parameter.
//
// In Swift, the method returns `()` (aka nil literal), and instead of passing
// the error in a pointer, it `throws`, so you have to wrap the call in a `do`
// statement and catch the error (or suppress error propagation via `try!`):
do {
try NSFontCollection.showFontCollection(collection: NSFontCollection,
withName: "Visible to All Users", visibility: .Computer)
}
catch {
print("There was an error showing font collection. Info: \(error)")
}
To add a user-visible font collection:
Repeat above steps, substituting .Computer with .User:
do {
try NSFontCollection.showFontCollection(collection: NSFontCollection,
withName: "Visible to the Current User", visibility: .User)
}
catch {
print("There was an error showing font collection. Info: \(error)")
}
To add a non-persistent font collection:
Repeat above steps, substituting .Computer with .Process:
do {
try NSFontCollection.showFontCollection(collection: NSFontCollection,
withName: "Visible for Current Process", visibility: .Process)
}
catch {
print("There was an error showing font collection. Info: \(error)")
}
Next steps…
Once you have a collection, you can change it all you want using the NSMutableFontCollection class. Continuing with the example above, you would do something like this:
let mutableCollection = collection.mutableCopy() as! NSMutableFontCollection
let boldTrait = [NSFontWeightTrait: NSFontWeightBold]
let boldAttributes = [NSFontTraitsAttribute: boldTrait]
let boldDescriptor = NSFontDescriptor(fontAttributes: newAttributes)
mutableCollection.addQueryForDescriptors([boldDescriptor])
At this point, the API gets weird again. We've added descriptors to our "named collection," but nothing in the UI is going to show up until you "show" the font collection again. In other words, after making any changes, you have to call showFontCollection(_:withName:visibility:) again.
Likewise, if you want to remove/delete a collection, you have to call hideFontCollectionWithName(_:visibility:). Despite its innocuous name, this method completely removes a persistent collection from disk, so be careful.
Next next steps…
In subsequent launches of your app, you can retrieve any persistent collection using the NSFontCollection(name:visibility:) method, like so:
// Retrieve collection created at an earlier time
let collectionOnDisk = NSFontCollection(name: "Visible to All Users", visibility: .Computer)
I think I've covered most of it, but if I've missed something, or if you have questions, just let me know. Good luck!
There are classes NSFontCollection and NSFontDescriptor.
Look into the NSFontManager header file of Xcode 7 (via ⇧⌘O) to get more information about the deprecated methods and their replacement.

In OSX accessing (optional?) property "identifier" of NSView subclass leads to bad access

Since I am fairly new to Swift programming on OSX, this question may contain several points that needs clarification.
I have a method which iterates over all subviews of a given NSView instance. For this, I get the array of subviews which is of type [AnyObject] and process one element at a time.
At some point I would like to access the identifier property of each instance. This property is implemented from a protocol in NSView named NSUserInterfaceItemIdentification, which type is given in the documentation as (optional) String?. In order to get that identifier I would have written
var view : NSView = subview as NSView;
var viewIdent : String = view.identifier!;
The second line is marked by the compiler with an error stating that identifier is not of an optional type, but instead of type String, and hence the post-fix operator ! cannot be applied.
Removing this operator compiles fine, but leads to a runtime error EXC_BAD_ACCESS (code=1, address=0x0) because identifier seems to be nil for some NSButton instance.
I cannot even test for this property, because the compiler gives me a String is not convertible to UInt8 while I try
if (view.identifier != nil) {viewIdent = view.identifier;}
My questions are
Is the documentation wrong? I.g. the property identifier is not optional?
How can I ship around this problem and get code that runs robust?
If the documentation states that view.identifier is an Optional, it means it can be nil. So it's not a surprise that for some button instances it is indeed nil for you.
Force unwrapping this element that can be nil will lead your app to crash, you can use safe unwrapping instead:
if let viewIdent = view.identifier {
// do something with viewIdent
} else {
// view.identifier was nil
}
You can easily check the type of an element in Xcode: click on the element while holding the ALT key. It will reveal a popup with informations, including the type. You can verify there that your element is an Optional or not.
Tip: you can safe unwrap several items on one line, it's rather convenient:
if let view = subview as? NSView, viewIdent = view.identifier {
// you can do something here with `viewIdent` because `view` and `view.identifier` were both not nil
} else {
// `view` or `view.identifier` was nil, handle the error here
}
EDIT:
You have to remove this line of yours before using my example:
var viewIdent : String = view.identifier!
Because if you keep this line before my examples, it won't work because you transform what was an Optional in a non-Optional by adding this exclamation mark.
Also it forces casting to a String, but maybe your identifier is an Int instead, so you shouldn't use this kind of declaration but prefer if let ... to safe unwrap and cast the value.
EDIT2:
You say my example doesn't compile... I test every answer I make on SO. I tested this one in a Playground before answering, here's a screenshot:
Also, after checking it, I confirm that the identifier is an Optional String, that's the type given by Xcode when using ALT+CLICK on the property. The documentation is right.
So if it's different for you, it means you have a different problem unrelated to this one; but my answer for this precise question remains the same.

Passing NSTextField Pointer to IOUSBInterfaceInterface182 Callback

I'm doing an asynchronous read from a USB printer. The read works correctly. My trouble is updating a NSTextField from within the callback.
-(IBAction)printTest:(id)sender
{
// Setup... then:
NSLog(#"starting async read: %#", _printerOutput);
NSLog(#"_printerOutput pointer = %p", _printerOutput);
result = (*interface)->ReadPipeAsyncTO(interface,
1,
readBuffer,
numBytesRead,
500,
1000,
USBDeviceReadCompletionCallback,
&(_printerOutput)
);
The callback is defined as:
void USBDeviceReadCompletionCallback(void *refCon, IOReturn result, void *messageArg)
{
NSTextField *printerOutput = (__bridge NSTextField *) messageArg;
NSLog(#"_printerOutput pointer = %p", printerOutput);
}
The pointer loses its value when inside of the callback.
starting async read: <NSTextField: 0x10221dc60>
_printerOutput pointer = 0x10221dc60
_printerOutput pointer = 0x0
I've looked in many places trying to mimic different ways to pass in the pointer. There can be only one correct way. :)
Another variation on the theme: (__bridge void *)(_printerOutput). This doesn't work, either.
I understand that the callback is of type IOAsyncCallback1.
Other URLs of note:
http://www.google.com/search?client=safari&rls=en&q=another+usb+notification+example&ie=UTF-8&oe=UTF-8 and updating UI from a C function in a thread
I presume _printerOutput is an NSTextField*?
First, is there a particular reason why are you passing an NSTextField** into the callback? (Note the ampersand in the last argument you're passing to ReadPipeAsyncTO.)
Second, I'd avoid ARC with sensitive code, just as a precaution.
Third, from what I see, last argument of ReadPipeAsyncTO is called refcon. Is it a coincidence that callback's first argument is called refCon? Note you're trying to get a text field from messageArg, not refCon.
To extend on my third point…
ReadPipeAsyncTO has an argument called refcon. This is the last argument.
Please pass _printerOutput there. Not a pointer to _printerOutput (do not pass &(_printerOutput)) -- _printerOutput is already a pointer.
Now finally. Look at the first argument of the callback. It's called refcon. In fact -- let's see what Apple docs say about this callback:
refcon
The refcon passed into the original I/O request
My conclusion is that your code should read:
void USBDeviceReadCompletionCallback(void *refCon, IOReturn result, void *messageArg)
{
NSTextField *printerOutput = (__bridge NSTextField *) refCon; // <=== the change is here
NSLog(#"_printerOutput pointer = %p", printerOutput);
}
Can you, please, try this out? I get a feeling that you didn't try this.
Small but possibly important digression: Were it some other object, and if you didn't use ARC, I'd suggest retaining the _printerOutput variable when passing it into ReadPipeAsyncTO, and releasing it in the callback.
But, since the text field should, presumably, have the lifetime of the application, there is probably no need to do so.
ARC probably loses track of the need for the object behind the pointer to exist once it's passed into C code, but it doesn't matter, since the pointer is still stored in the printerOutput property. Besides, once a pointer is in C code, nothing can just "follow it around" and "reset it".
Confusion when it comes to understanding and explaining the concepts is precisely why I said "avoid ARC with sensitive code". :-)

How do I get the line of text under the cursor in a TextView in gtk#?

I have a GTK# TextView and I want to read the line of text under the cursor. I don't see a single method that will do that, so I think I need to combine several method calls, like Buffer.GetText, Buffer.GetIterAtOffset, Buffer.CursorPosition, but it's not obvious to me what the right combination is.
TextIter are a bit odd to use. Buffer.CursorPosition gives you the current position.
It's easy to find the end of the line:
var end = Buffer.CursorPosition;
end.ForwardToLineEnd();
To get the first character, there's not symetrical method, so you might try:
var start = Buffer.CursorPosition;
start.BackwardChars(start.LineOffset); // LineOffset gives you the iter offset on the current line.

Resources