I'm trying to overwrite the #new message in MyObject. The problem is that when the text gets compiled, the local variables, disp and oldNew are changed to t1 and t2 respectively (I'm using Squeak 4.3) and then it can't send oldNew to self.
I could change their names but I'm not sure that's a good idea.
Here's a basic outline of what I have:
MyObject class methodDict at: #new put:
(Object compilerClass new
compile: 'new
| disp oldNew |
oldNew := MyObject class methodDict at: #new.
disp := Dispatcher new.
^disp xxxViewedObject: self oldNew'
in: MyObject
notifying: nil
ifFail: []) generate
I'm not 100% sure if what I'm doing is the right way to do it so other ideas are welcome.
Edit: OK so I realise now it was looking for oldNew as a message in MyObject, but then how do I run the compiled method?
Apparently my problem was that MyObject was a subclass of ProtoObject and is now a subclass of Object.
Here's the code that seems to work after this change:
MyObject class methodDict at: #new put:
(Object compilerClass new
compile: 'new
| disp |
disp := Dispatcher new.
^disp xxxViewedObject: self basicNew initialize'
in: MyObject
notifying: nil
ifFail: []) generate
To evaluate your new generated compiled method you may use:
aCompiledMethod valueWithReceiver: nil arguments: #()
That's a nice way, however if you're experimenting problems I wrote a "code generator" based in a cross-Smalltalk library called Grease and which can be useful for you. It manages auto-comments, RBParser and Parser, authoring, and basic templating. All can be extended by anyone of course.
Generated methods are no different than others. So you simply send the method's selector to invoke it:
var := MyObject new.
Related
I'm attempting to create a class in Swift 3 to implement a Cordova plugin. I have this building and running, but the application crashes whenever any properties of the class are accessed. I've tried two ways of initializing the class:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players = [UUID:DSFPlayerHandler] ();
...
}
and
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler];
override init () {
players = [:];
}
...
}
However, when my players property is used, the result is a EXC_BAD_ACCESS exception, with an address that looks like a null pointer dereference.
The object is being created by Objective C code, which is a language I have no familiarity with at all, but I think this is the line that creates it:
obj = [[NSClassFromString(className)alloc] initWithWebViewEngine:_webViewEngine];
The CDVPlugin class contains a comment stating that initWithWebViewEngine should not be overridden (and indeed I do not seem to be able to override this method, because while it is declared in the CDVPlugin.m file, it isn't mentioned in CDVPlugin.h, so the Swift compiler doesn't seem to know about it), but rather initialization code should be placed in a method called pluginInitialize instead. However, if I do that I get a compiler error ("Class DSFMediaCentre has no initializers").
Furthermore, if I put my init() method back in and set it to call pluginInitialize(), like this:
override init () {
super.init(); // necessary otherwise next line is an error
pluginInitialize();
}
override func pluginInitialize() {
players = [:];
}
the error then changes to "Property 'self.players' not initialized at super.init call".
How do I make this class initialize correctly?
You have a mismatch between the strict initialization system required by the language and the procedure used by the framework you're working with.
Swift demands that a) properties be initialized as part of object construction, and b) that construction be chained to the type's supertype. But the CDVPlugin type is doing the construction on your behalf; you don't have the ability to customize it. (This makes more sense in ObjC, because it doesn't have the same compile-time restrictions as Swift.)
The situation is similar to unpacking an object from a nib file. In that case too, because it's the nib loading system that's constructing your object, you don't have the ability to customize the initializer. Your type will always be constructed by init(coder:). In a certain sense, your initialization point moves further down, to awakeFromNib(), and among other things, that forces outlets to other objects in the archive to be declared as optional, usually implicitly unwrapped.
The same solution should avail you here. You should consider pluginInitialize() to be your initialization point. The language then requires that properties be optional, since they are not filled at its initialization point. Therefore, make the property an IUO:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler]!
override func pluginInitialize() {
players = [:];
}
}
and all should be well.
The other solution is to use lazy keyword
lazy var players :[UUID:DSFPlayerHandler] = [:]
So, you don't need to initialize players in initializer but still make sure players always non-nulable
Lets say I've created a class MyClass in Pharo Smalltalk...
If I in Workspace write:
MyClass new.
and select print-it (Ctrl-P), I get:
A MyClass
With a bit of tinkering with MyClass's printOn: method,
I could get a bit more, for example:
A MyClass with value: 5
+++
So comes my question... How can I make a test (instance of TestCase-class)
that checks that the textual-representation of MyObject - what I would get
if I did "MyObject new" and Print-It - is what it's supposed to be?
How do I get the textual representation so I can check it against a
string-constant with what it should be, when I do a self assert: equal: (or something similar) in my test?
For example that after using my cutomized printOn: method, it will
look something like
A MyClass with value: 5
Sorry for such a newbie question, but there goes...
To get the textual representation of an object you can send the message printString to the object. For example Object new printString will return you the string 'an Object'.
To create a test case you should create a subclass of TestCase:
TestCase subclass: #MyClassTestCase
instanceVariableNames: ''
classVariableNames: ''
package: 'MyTest-Package'
Then a test is a method that begins with test. For example the following test verifies the string representation of Object new:
testClassRepresentation
self assert: Object new printString equals: 'an Object'
The behaviour can be observed by placing this code in a playground:
import Foundation
import ObjectiveC
class TestClass {}
var obj = TestClass()
let stringValue = "xyz"
let key = "def"
objc_setAssociatedObject(obj, key, stringValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
//let f = "f"
let returnedString = objc_getAssociatedObject(obj, key)
This works and returns "xyz" from the objc_getAssociatedObject call.
However, if you remove the comment from the let f = "f" line. The objc_getAssociatedObject call now returns nil.
I'm baffled as to how setting a totally unrelated variable can effect the call.
Any ideas?
Looks like a bug.
The objc_... methods are part of the Objective-C runtime. They shouldn't exist in Swift.
That said they clearly do. So my guess is that there's something happening when you set that method that kicks the runtime, similar to calling synchronize on NSUserDefaults.
Edit: This NSHipster article explains that the ObjC runtime is actually there.
Edit 2: I tried a few experiments, and I'll make your question even weirder. Wrapping the test case inside the object yields the same result. But changing the variable name to an underscore fixes the problem:
let _ = "f"
I bet assigning a variable overwrites whatever runtime associations you set manually. The underscore just tells the compiler that you aren't using the result of the assignment.
Compiling in XCode 3.1.3 using GCC 4, under Leopard 10.5.8, with 10.5 as my target...
I have an interface, thus:
#interface testThing : NSObject { classContaininghooHa *ttv; }
#end
And an implementation, thus:
#implementation: testThing
- (void) instanceMethodMine
{
[ttv hooHa]; // works perfectly, compiles, links, hooHa is invoked.
}
// void cFunctionMine()
// {
// [ttv hooHa]; // compiler: 'ttv' undeclared (first use in this function) "
// }
void stupidCFunctionMine((testThing *)whom) // whom is passed class 'self' when invoked
{
[whom instanceMethodMine]; // compiles, links, works. :/
}
#end
Now, my understanding -- clearly flawed -- was that if you declared a variable, class ID or otherwise, it was private to the class, but within the class, is performed essentially as a global, stored in the allocated class instance for the duration of its existence.
That's how it acts for objc methods.
But in the c function above, also written within the class, the variable appears to be invisible. The doesn't make sense to me, but there it is.
Can someone explain to me what is going on?
While you're at it, how can I declare this as an instance variable so I can use the method within a c function declared within the class scope as shown above, as well as within methods?
Insight much appreciated.
It doesn't make any difference where you are declaring/defining your "normal" c functions. They are not part of the class, they are just plain old c functions. No connection to the class whatsoever. Passing the instance they work on is a workaround if you really don't want to make this function a true objective-c method.
interface methods have full access to it's member variables. And the C function is not part of the class and so it cannot access any class variables unless it takes an class instance as the argument.
void cFunctionMine()
{
[ttv hooHa]; // compiler: 'ttv' undeclared (first use in this function)
}
Clearly cFunctionMine is not part of the interface. So it does not what ttv is to send the message hooHa.
While you're at it, how can I declare this as an instance variable so I can use the method within a c function declared within the class scope as shown above, as well as within methods?
void cFunctionMine()
{
// 1. Create an instance using alloc and init
testThing *ttv = [ [testThing alloc] init ] ;
[ttv hooHa] ;
// Now the above statement is valid. We have a valid instance to which
// message can be passed to.
// .....
[ ttv release ] ;
// release the resources once you are done to prevent memory leaks.
}
I'm having trouble in getting the singleton pattern to initialize a instance variable in smalltalk. (here is a link to another implementation for clarification)
this is what I have:
new
^UniqueInstance ifNil: [UniqueInstance := self basicNew.
UniqueInstance: instanceVar := Object new. ].
that last line (UniqueInstance: instanceVar := Object new.) doesn't work, but that's basically what I need to do: instantiate instanceVar as an Object before returning UniqueInstance back to the caller.
Notice that this 'new' method is used as a classinstantiation, and that libraries is a instance variable of UniqueIsntance (the isntance of the wanted class).
Can anyone point me in the right direction?
Try simpler:
YourClass class>>singleton
UniqueInstance ifNil: [UniqueInstance := self basicNew initialize].
^UniqueInstance
then on instance side of your class implement an appropriate #initialize method, for example:
YourClass>>initialize
someInstvar := someInitalValue.
^self
Update:: Name of the class method accessing the singleton varies, it can be #default, #current, or #singleton. I mostly use later.