Generalising Cocoa Object Creation

In creating a universal handler that can instantiate new cocoa class objects using an appropriate type method, there are three ways I can see of doing this, but was wondering whether anyone had any wisdom as to which technique would be more recommended (or which technique would not be), + ⁄ – reasons. Or are there are other ways I ought to be looking at instead ?

1. performSelector_withObject_ using a type method as selector

NSArray's performSelector_withObject_("arrayWithArray:", {1, 2, 3})

2. performSelector_withObject_ using an AppleScript handler as selector

performSelector_withObject_("__NSArray_:", {1, 2, 3})

to __NSArray__(obj)
	try
		obj's allObjects()
	on error
		obj
	end try
	NSArray's arrayWithArray:result
end __NSArray__

3. NSExpression with type method as selector

tell NSExpression
	set object to expressionForConstantValue_({1, 2, 3})
	set _class to expressionForConstantValue_(NSArray)
		
    (its expressionForFunction:_class ¬
		selectorName:("arrayWithArray:") ¬
        arguments:{object})'s ¬
		expressionValueWithObject:(missing value) ¬
        context:(missing value)
end tell

Note: These are incomplete snippets that won’t compile by themselves. This header should be sufficient if added to any of the above:

use framework "Foundation"

property this : a reference to the current application
property NSArray : a reference to NSArray of this
property NSExpression : a reference to NSExpression of this

I’m having trouble reconciling your topic title and description of your aim with your code. Any chance of elaborating on what you’re trying to achieve?

Sounds interesting! Please post a little essay about how this works. I still have no idea how to start using Obj-C with AS, even though I am competent in both. There has to be a manual somewhere. Oh wait, I used to say that about Core Data, too … and had to wait years for the Stanford videos to actually explain it.

Maybe this will result in a huge library or framework we can load and have a much more compact AS-ObjC language.

Of course (I struggled with the title, so I can imagine it’s far from apt).

Assume with any snippets below that I will already have imported the appropriate framework to use in an AppleScript, i.e. Foundation + ⁄ – AppKit; and any classes I refer to will be defined as top-level script properties (because I hate typing out current application more than once).

Given this, in AppleScriptObjC, one can create an NSArray using an AppleScript list object. I almost always do it like this:

set L to {true, 2, {pi, "four"}, {5}} 
set arr to NSArray's arrayWithArray:L

It can get repetitive. Then when I need to create an NSDictionary, that really gets on my tits, because I can never write dictionaryWithDictionary: correctly the first time. But, essentially, there’s a lot of these statements cropping up ad nauseum in virtually every ASObjC script:

set dict to NSDictionary's dictionaryWithDictionary:reco
set str to NSString's stringWithString:txt
set fURL to NSURL's fileURLWithPath:fp

Initially, I simply created an AppleScript handler—one for each class of object—that takes an AppleScript object and spits out a Cocoa object (reference):

to __NSString__(txt as text)
    NSString's stringWithString:txt
end __NSString__

…and so on, so creating, for example, an NSArray would now be possible with:

set arr to __NSArray__({true, 2, {pi, "four"}, {5}})

An NSURL pointing to a local file URL is now got like so:

set fURL to __NSURL__("~/path/to/file")

where the handler for this looks something like:

to __NSURL__(filepath as text)
	NSURL's fileURLWithPath:((NSString's ¬
		stringWithString:filepath)'s ¬
		stringByStandardizingPath())
end __NSURL__

Now, I’m looking to have one handler that will take an AppleScript object, which can be any of those classes above, and give back the appropriate Cocoa reference. The three examples from my first post illustrate what a proposed handler would be doing when passed a list and the name of Cocoa class I’m expecting back. For example, I can keep the AppleScript handlers above and use those as selectors, passing its name and the parameter to performSelector_withObject_():

to make _nsClass as text with data obj as {record, text, list} : missing value
	local _nsClass, obj
	
	if obj = missing value then tell the ¬
		current application to return ¬
		NSClassFromString(_nsClass)'s new()
	
	set SEL to ["__", _nsClass, "_:"] as text
	performSelector_withObject_(SEL, object)
end make

Then it’s possible to Cocoa-fy an AppleScript objects like this:

set arr to make "NSArray" with data {1, 2, 3}
set str to make "NSURL" with data "~/path/to/file"

and if one just wants to initialise an instance of a Foundation type to be returned empty—say, NSMutableArray—then:

set arr0 to make "NSMutableArray"
arr0's addObject:"foo"
arr0 as list --> {"foo"}

I’m looking for insight and/or advice about whether this is a suitable way to implement this generalised AppleScript-to-Cocoa handler, to which I’m referring to the use of performSelector_withObject_ to achieve this (and not so much to the use of make I imagine would have Nigel’s eyes rolling a full 360°); or whether one of the other two techniques from my original post are more/less suitable/recommended (I can provide the form that the generalised handlers for those two would take if the specific examples aren’t sufficient to extrapolate this, but basically they look up the name of the Foundation class’s type method used to instantiate and initialise the corresponding Cocoa object, and pass that as the selector used either by performSelector_withObject_ or NSExpression's expressionForFunction:selectorName:arguments:); or, of course, if there’s another way (NSInvocation, maybe ?).

I know this isn’t your main point, but: You do know that this is covered by code-completion? You should rarely need to type a method in full. (I say this as a notoriously poor typist.)

I’m old-school so I prefer simple:

on makeChocky:someThing
	return (current application's NSArray's arrayWithObject:someThing)'s firstObject()
end makeChocky:

set x to my makeChocky:{1, 2, 3}
set y to my makeChocky:(POSIX file "~/path/to/file")

There’s a little bit of extra overhead, but not nearly as much as the sort of handlers you’re playing with. For mutability you could do:

on makeMutableChocky:someThing
	return (current application's NSArray's arrayWithObject:someThing)'s firstObject()'s mutableCopy()
end makeMutableChocky:

That made me facepalm for not thinking about this. Cheers for this, it would seem to cover the most common situations pretty easily.

How would you extend this or what other method would you employ (not necessarily you personally, but “one”) to handle a cocoa type that doesn’t bridge to an AppleScript equivalent ? Things like NSPredicate, NSExpression, NSPasteboard, NSBundle, NSSortDescriptor, etc.?

What order of magnitude are the proposed methods I described likely to occur measured against going the conventional routes ? And would performSelector... be a more expensive operation than using NSExpression, or are they comparable ?

I don’t see how they lend themselves to this sort of treatment. I mean, they have their constructors, but generally it makes more sense to use their convenience methods to create and configure at the same time.

You’d have to time them. The very quick tests I did suggested times were between 4x and 8x, whereas the handler I posted was about 1.3x. But these were very simple tests. I think your #2 was the worst performer.

Script Geek.app is pretty good for timing this stuff.

Sorry for the delay. Been in hospital. But thank you for your advice and insight, @ShaneStanley.