Resolving strong references between Swift and Objective-C classes – Using unowned and weak references from Swift to Objective-C classes

Pete Barber from C#, C++, Windows & other ramblings

My Swift and SpriteKit exploration continues. At the moment I'm writing the collision handling code.

Rather than derive game objects from SKSpriteNode with each derived class containing the code for handling collisions with the other types of game objects I'm following something akin to a Component-Entity model.

I have per-game-object handler classes of which an instance of each is stored in the actual SKSpriteNode's userData dictionary. In turn each handler instance has a reference to the SKSpriteNode that references it. Given ARC is used this is a classical strong-reference chain which will prevent memory from being freed. The usual solution to this in Objective-C is to have one of the references be weak. In Swift there are two types of weak references: weak which is the same as in Objective-C and unowned (which I think is new). The difference is that an unowned reference can never be nil, i.e. it's optional whether a weak pointer reference an object but an unowned pointer must always reference something. As such the member variable is always defined using let and must be initialized, i.e. an init method is required.

The following code shows how I was intending to implement this. There is the strong reference from node.UserData to PhysicsActions and then the unowned reference back again from PhysicsActions.

class PhysicsActions
{
  unowned let node : SKSpriteNode

  init(associatedNode : SKSpriteNode)
  {
    // Store really weak (unowned) reference
    self.node = associatedNode
  }

  func onContact(other : PhysicsActions)
  {
     // Do stuff with node

  }
}

class func makeNode(imageNamed name: String) -> SKSpriteNode
{
  let node = SKSpriteNode(imageNamed: name)
  node.userData = NSMutableDictionary()
  // Store strong reference
  node.userData["action"] = makeActionFn(node)
  return node

}

However, when I went to use this code it crashed within the onContact method when it attempted to use the node. Changing this the reference type from unowned to weak fixed this, e.g.

weak let node : SKSpriteNode?

This verified that the rest of the code was ok so this seemed to look like another Swift/Objective-C interoperability issue. Firstly, I made a pure Swift example which is a simplified version from the The Swift Programming Language book.

class Foo
{
  var bar : Bar?

  func addBar(bar: Bar)
  {
    self.bar = bar
  }
}

class Bar
{
  unowned let foo : Foo
  init(foo : Foo)
  {
    self.foo = foo
  }

  func f()
  {
    println("foo:\(foo)")
  }
}

var foo : Foo? = Foo()
var bar = Bar(foo: foo!)
foo!.ç(bar)

bar.f()

Which works and results in:

foo:C14XXXUnownedTest3Foo (has 1 child)

Ok, not a fundamental problem but let's try having an unowned reference to an Objective-C class which is just like the real case as that's what SKSpriteNode is.

Foo2.h

@interface Foo2 : NSObject
@end

Foo2.m

@implementation Foo2

-(id)init
{
  return [super init];
}

@end

main.swift

class Bar2
{
  unowned let foo2 : Foo2
  init(foo2 : Foo2)
  {
    self.foo2 = foo2
  }

  func f()
  {
    println("foo2:\(foo2)")
  }
}

var foo2 = Foo2()
var bar2 = Bar2(foo2: foo2)
bar2.f()

Which when foo2.f() is invoked results in:

libswift_stdlib_core.dylib`_swift_abortRetainUnowned:
0x100142420:  pushq  %rbp
0x100142421:  movq   %rsp, %rbp
0x100142424:  leaq   0x17597(%rip), %rax       ; "attempted to retain deallocated object"
0x10014242b:  movq   %rax, 0x348be(%rip)       ; gCRAnnotations + 8
0x100142432:  int3   
0x100142433:  nopw   %cs:(%rax,%rax)

Again, changing unowned let foo2 : Foo2 to weak var foo2 : Foo2? works. 

I can't explain what the actual problem is but it looks like the enhanced weak reference support (unowned) only works with pure Swift classes. If you have cyclic references to Objective-C classes from Swift then don't use unowned. In fact writing the previous sentence led me to try the following:

let orig = Foo2()
unowned let other = orig
println("other:\(other)")
println("orig:\(orig)")

No cyclic references, just an ordinary reference counted instance of Foo2 (the Objective-C class) which is then assigned to an unowned reference. The final call to println will keep the instance around until the end. This crashes as per the previous example when the other variable is accessed. Changing the type assigned to orig from Foo2 (Objective-C) to Foo (Swift) make it work.

Therefore it seems unowned should not be used to refer to Objective-C classes, just Swift classes.



Beware: Despite the docs SKNode userData is optional

Pete Barber from C#, C++, Windows & other ramblings

In the Swift documentation for SKNode the userData member (a dictionary) is defined as follows:

userData

A dictionary containing arbitrary data.

Declaration

SWIFT
var userData: NSMutableDictionary!

OBJECTIVE-C

@property(nonatomic, retain) NSMutableDictionary *userData

However, in Objective-C the userData member is initially nil. Given that this is the same class then it should also be in Swift and using it, e.g.

let node = SKSpriteNode(imageNamed: name)
node.userData["action"] = Action() // custom class

causes a crash:

fatal error: Can't unwrap Optional.None

This is because the it is in fact nil despite the '!' following the Swift definition. This must be a documentation bug. The correct code is:

let node = SKSpriteNode(imageNamed: name)
node.userData = NSMutableDictionary()
node.userData["action"] = Action() // custom class

Integration Testing with NUnit and Entity Framework

Pete Barber from C#, C++, Windows & other ramblings

This post gives a quick introduction into creating SQL CE dBs for performing Integration Tests using NUnit.

In the previous post Using NUnit and Entity Framework DbContext to programmatically create SQL Server CE databases and specify the databse directory a basic way was shown to how to create a new dB (using Entity Framework's DbContext) programmtically.  This was used to generate a new dB for a test hosted by NUnit.

The subsequent post Generating a SQL Server CE database schema from a SQL Server database using Entity Framework showed how to generate a SQL CE dB schema from an existing SQL Server database.

This post ties theprevious ones together.  As mentioned in the first post the reason for this is an attempt at what amounts to Integration Testing using NUnit.  I'm currently building a Repository and Unit Of Work abstraction on top of Entity Framework which will allow the isolation of the dB code (in fact it will isolate and abstract away most forms of data storage).  This means any business logic can be tested with a test-double that implements the Repository and UnitOfWork interfaces; which is straight forward Unit Testing.  The Integration Testing is to verify that the Repository and Unit Of Work implementations work correctly.

The rest of the post isn't focused on these two patterns; though it may mention them.  Instead it documents my further experience of using NUnit to writes tests that interact with dB via Entity Framework.  The premise for this is that a dB already exists.

As such the approach to using Entity Framework is a hybrid of Database First and Code First in that the dB schema exists and needs be maintained outside of EF and also that EF should not generate model classes, i.e. allowing the use of Code First POCOs.  This is possible as the POCOs can be defined, a connection made to dB and then the two are conflated via an EF DbContext.  It then seems that EF creates the model on the fly (internally compiles it) and as long as the POCO types map to the dB types then it all works as if by magic!

The advantage of doing it this way is that the existing dB is SQL Express based but for the Integration Testing a new dB can be created when needed, potentially one per test.  In order to keep the test dBs isolated from the real dB SQL Server Compact Edition (SQL Server CE V4) was used.  Therefore the requirement was for the EF code to be able to work with SQL Express and SQL CE with the primary definition of the schema taken from SQL Express.  It's not possible to use exactly the same schema as SQL CE only has a subset of the data-types provides by SQL CE.  However, the process described in the post 
Generating a SQL Server CE database schema from a SQL Server database using Entity Framework showed how to create semantically equivalent SQL.


From this point onwards it's assumed that an SQL file to create the dB has been generated.  Now create a new C# class library project and using the NUGet add Entity Framework, NUnit and SQL CE 4.0.  All my work has been with EF 4.3.1.  Following this drag the Model1.edmx.sqlce file from the project used to generate to new project.  You may wish to rename it, e.g. to test.sqlce.


Creating the database

The post Generating a SQL Server CE database schema from a SQL Server database using Entity Framework showed how to create a new CE dB per-test using the EF DbContext to do the hard work.  A different approach is now taken as the problem with creating a dB using DbContext is that in addition to creating any specified tables and indices etc. it also creates an additional table called '__MigrationHistory' which contains a description of the EF model used to create the dB.  The description of the problem caused by this will be delayed until the "Why DbContext is no longer used to create the database" section.  Suffice to say for the present using the new mechanism avoids the creation of this table.

The code below is the beginnings of a test class.  It is assumed all the tests need a fresh copy of the dB hence the creation is performed in the Setup method.  All this code does is create a SQL CE dB and then
creates the schema.

1:  [TestFixture]  
2: public class SimpleTests
3: {
4: const string DB_NAME = "test.sdf";
5: const string DB_PATH = @".\" + DB_NAME;
6: const string CONNECTION_STRING = "data source=" + DB_PATH;
7: [SetUp]
8: public void Setup()
9: {
10: DeleteDb();
11: using (var eng = new SqlCeEngine(CONNECTION_STRING))
12: eng.CreateDatabase();
13: using (var conn = new SqlCeConnection(CONNECTION_STRING))
14: {
15: conn.Open();
16: string sql=ReadSQLFromFile(@"C:\Users\Pete\work\Jub\EFTests\Test.sqlce");
17: string[] sqlCmds = sql.Split(new string[] { "GO" }, int.MaxValue, StringSplitOptions.RemoveEmptyEntries);
18: foreach (string sqlCmd in sqlCmds)
19: try
20: {
21: var cmd = conn.CreateCommand();
22:
23: cmd.CommandText = sqlCmd;
24: cmd.ExecuteNonQuery();
25: }
26: catch (Exception e)
27: {
28: Console.Error.WriteLine("{0}:{1}", e.Message, sqlCmd);
29: throw;
30: }
31: }
32: }
33: public void DeleteDb()
34: {
35: if (File.Exists(DB_PATH))
36: File.Delete(DB_PATH);
37: }
38: private string ReadSQLFromFile(string sqlFilePath)
39: {
40: using (TextReader r = new StreamReader(sqlFilePath))
41: {
42: return r.ReadToEnd();
43: }
44: }
45: }
46:
The dB file (Test.sdf) will be created in the current working directory.  As the test assembly is located in <project>\bin\debug which is where the NUnit test runner picks up the DLL from this directory this is where it is created.  If a specific directory is required then the '.\' can be replaced with the required path.

The Setup method is marked with NUnit's SetUp attribute meaning it will be invoked on a per-test basis creating a new dB instance for each test.  The DeleteDb method could be marked with [TearDown] attribute but at the moment any previous dB is deleted before creating a new one.  It would be fine to do both as a belt and braces approach.  The reason I didn't make it the TearDown method is so that I could inspect the dB following a test if needed.

SQL CE does not support batch execution of SQL scripts which is where it gets interesting as the SQL generated previously is in batch form.  The code reads the entire file into a string and determines each individual statement by splitting string on the 'GO' command that separates each SQL command.

To help understand the SQL the following is the diagram of the dB I'm working with.  All fields are strings except for the Ids which are numeric.
Each of these commands is then executed.  The previously generated SQL (the SQL for the dB I'm working with is below) will not work completely out of the box.  The ALTER and DROP statements at the beginning don't apply as the schema is being applied to an empty dB, these should be removed.  Interestingly the schema generation step for my dB seems to miss out a 'GO' between the penultimate and ultimate statement.  I had to add one by hand.  Finally, the comments at the end prove a problem as there is no terminating 'GO'.  Removing these fixes the problem.  In the code above the exception handler re-throws the exception after writing out the details.  For everything to proceed the SQL needs modifying to execute perfectly.  If the re-throw is removed then the code will tolerate individual command failures which in this context really just amount to warnings.

NOTE: Text highlighted in red has been removed and text in blue added.

-- --------------------------------------------------
-- Entity Designer DDL Script for SQL Server Compact Edition
-- --------------------------------------------------
-- Date Created: 07/29/2012 12:28:35
-- Generated from EDMX file: C:\Users\Pete\work\Jub\DummyWebApplicationToGenerateSQLServerCE4Script\Model1.edmx
-- --------------------------------------------------


-- --------------------------------------------------
-- Dropping existing FOREIGN KEY constraints
-- NOTE: if the constraint does not exist, an ignorable error will be reported.
-- --------------------------------------------------

    ALTER TABLE [RepComments] DROP CONSTRAINT [FK_RepComments_Reps];
GO

-- --------------------------------------------------
-- Dropping existing tables
-- NOTE: if the table does not exist, an ignorable error will be reported.
-- --------------------------------------------------

    DROP TABLE [RepComments];
GO
    DROP TABLE [Reps];
GO
    DROP TABLE [Roads];
GO

-- --------------------------------------------------
-- Creating all tables
-- --------------------------------------------------

-- Creating table 'RepComments'
CREATE TABLE [RepComments] (
    [CommentId] int IDENTITY(1,1) NOT NULL,
    [RepId] int  NOT NULL,
    [Comment] ntext  NOT NULL
);
GO

-- Creating table 'Reps'
CREATE TABLE [Reps] (
    [RepId] int IDENTITY(1,1) NOT NULL,
    [RepName] nvarchar(50)  NOT NULL,
    [RoadName] nvarchar(256)  NOT NULL,
    [HouseNumberOrName] nvarchar(50)  NOT NULL,
    [ContactTelNumber] nvarchar(20)  NOT NULL,
    [Email] nvarchar(50)  NULL
);
GO

-- Creating table 'Roads'
CREATE TABLE [Roads] (
    [Name] nvarchar(256)  NOT NULL
);
GO

-- --------------------------------------------------
-- Creating all PRIMARY KEY constraints
-- --------------------------------------------------

-- Creating primary key on [CommentId] in table 'RepComments'
ALTER TABLE [RepComments]
ADD CONSTRAINT [PK_RepComments]
    PRIMARY KEY ([CommentId] );
GO

-- Creating primary key on [RepId] in table 'Reps'
ALTER TABLE [Reps]
ADD CONSTRAINT [PK_Reps]
    PRIMARY KEY ([RepId] );
GO

-- Creating primary key on [Name] in table 'Roads'
ALTER TABLE [Roads]
ADD CONSTRAINT [PK_Roads]
    PRIMARY KEY ([Name] );
GO

-- --------------------------------------------------
-- Creating all FOREIGN KEY constraints
-- --------------------------------------------------

-- Creating foreign key on [RepId] in table 'RepComments'
ALTER TABLE [RepComments]
ADD CONSTRAINT [FK_RepComments_Reps]
    FOREIGN KEY ([RepId])
    REFERENCES [Reps]
        ([RepId])
    ON DELETE NO ACTION ON UPDATE NO ACTION;
GO
-- Creating non-clustered index for FOREIGN KEY 'FK_RepComments_Reps'
CREATE INDEX [IX_FK_RepComments_Reps]
ON [RepComments]
    ([RepId]);
GO

-- --------------------------------------------------
-- Script has ended
-- --------------------------------------------------

Getting the SQL into a state where it will run flawlessly is a little bit of a hassle but given the number of times it will be used subsequently it's job a big job, well for a small dB anyway.  To verify that your dB has been created as needed an quick and easy way to test is to comment out the call to DeleteDb() and after a test has run open to the dB using Server Explorer within VS, i.e.



Using the dB in a test

Now that a fresh dB will be created for each test it's time to look at simple test:

1:  [Test]  
2: public void TestOne()
3: {
4: using (var conn = new SqlCeConnection(CONNECTION_STRING))
5: using (var ctx = new TestCtx(conn))
6: {
7: ctx.Roads.Add(new Road() { Name = "Test" });
8: ctx.SaveChanges();
9: Assert.That(1, Is.EqualTo(ctx.Roads.Count()));
10: }
11: }
Road in this case is defined as:

1:  class Road  
2: {
3: [Key]
4: public string Name { get; set; }
5: }

The first thing to note is that EF is not used to form the connection to the dB, instead one is made using the SqlCe specific classes.  Attempting to get EF to connect to a specific dB instance when not referring to a named connection strings in the .config file is a bit of an art (I may write another entry about this).  However, EF is quite happy to work with an existing connection.  This makes for a good separation of responsibilities in the code where EF manages the interactions with the dB but the control of the connection is elsewhere.

NOTE: It is likely that each test will require a connection and a context hence rather it might make more sense to move the creation of the SqlCeConnection and the context (TestCtx in this case) to a SetUp method and as these resources need disposing of adding a TearDown method to do that.  TestCtx could also be modified to pass true to the DbContext constructor to give ownership of the connection to the context so that it will dispose of it then context is disposed off.

I would have preferred to avoid having to defined a specific derived context and instead use DbContext directory, e.g.
1:  [Test]  
2: public void TesTwo()
3: {
4: using (var conn = new SqlCeConnection(CONNECTION_STRING))
5: using (var ctx = new DbContext(conn, false))
6: {
7: ctx.Set<Road>().Add(new Road() { Name = "Test" });
8: ctx.SaveChanges();
9: Assert.That(1, Is.EqualTo(ctx.Set<Road>().Count()));
10: }
11: }

However when SaveChanges() is called the following exception is thrown:

System.InvalidOperationException : The entity type Road is not part of the model for the current context.

This is because EF knows nothing about the Road type.  When a derived context is created for the first time I think EF performs reflection on any properties that expose DbSet.  These are the types that form the Model.  Another option is to create the model, optionally compile it and then pass it to an instance of DbContext.  This way involves a lot less code.

That's it.  The final section is just footnote about the move away from using EF to create the dB.

Why DbContext is no longer used to create the database

As mentioned creating the dB using:
1:  using (var ctx = new DbContext("bar.sdf"))  
2: {
3: ctx.Database.Create();
4: // create schema etc.
5: }
causes the '__MigrationHistory' table to be created.  Assuming this method was used, later on when TestCtx was used top open the dB and perform an operation the following exception would be thrown:

System.InvalidOperationException : The model backing the 'DbContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).
This is because the context used to create the model was a raw DbContext (as per the previous post) whereas the dB was accessed via the TestCtx.  If the context used to create the dB is also changed to TestCtx then this problem goes away.
However, given the original dB is not intended to be created nor be maintained (code migrations) by EF then using the non-context/EF approach to dB completely removes EF from the picture.