diff --git a/applications/test/gravityMeshObject/Test-gravityMeshObject.C b/applications/test/gravityMeshObject/Test-gravityMeshObject.C
index f95a39fe49afcd50d420837cd78cabf8efbb4415..c6d93c7bc9d2c287bc580246622f6ed816b3bc15 100644
--- a/applications/test/gravityMeshObject/Test-gravityMeshObject.C
+++ b/applications/test/gravityMeshObject/Test-gravityMeshObject.C
@@ -5,7 +5,7 @@
     \\  /    A nd           | www.openfoam.com
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
-    Copyright (C) 2023 OpenCFD Ltd.
+    Copyright (C) 2023-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -50,6 +50,9 @@ void printInfo(const meshObjects::gravity& g)
 
 int main(int argc, char *argv[])
 {
+    argList::addBoolOption("checkout", "Test checkout with release");
+    argList::addBoolOption("release", "Test release instead of delete");
+
     #include "setRootCase.H"
     #include "createTime.H"
 
@@ -70,14 +73,81 @@ int main(int argc, char *argv[])
             printInfo(g);
         }
 
-        Pout<< "registered:" << flatOutput(runTime.sortedToc()) << nl << endl;
+        Pout<< "registered:"
+            << flatOutput(runTime.sortedToc()) << nl << endl;
+    }
+
+    std::unique_ptr<meshObjects::gravity> release1;
+    std::unique_ptr<meshObjects::gravity> release2;
+
+    if (args.found("release"))
+    {
+        // Ugly!
+        typedef
+            MeshObject<Time, TopologicalMeshObject, meshObjects::gravity>
+            parent_type;
+
+        release1 = meshObjects::gravity::Release("g", runTime);
+        release2 = meshObjects::gravity::Release("#none#", runTime);
+
+        Info<< "release: " << Switch::name(bool(release1))
+            << ", " << Switch::name(bool(release2)) << nl;
+
+        Info<< "after Release: "
+            << flatOutput(runTime.sortedToc()) << endl;
+
+        // Do checkout by hand (ugly)
+        if (args.found("checkout"))
+        {
+            if (release1)
+            {
+                release1->parent_type::checkOut();
+            }
+
+            if (release2)
+            {
+                release2->parent_type::checkOut();
+            }
+
+            Info<< "after checkout: "
+                << flatOutput(runTime.sortedToc()) << endl;
+        }
+    }
+    else if (args.found("checkout"))
+    {
+        // Do checkout as part of release
+        release1 = meshObjects::gravity::Release("g", runTime, true);
+        release2 = meshObjects::gravity::Release("#none#", runTime, true);
+
+        Info<< "release: " << Switch::name(bool(release1))
+            << ", " << Switch::name(bool(release2)) << nl;
+
+        Info<< "after Release/Checkout(true) : "
+            << flatOutput(runTime.sortedToc()) << endl;
+    }
+    else
+    {
+        meshObjects::gravity::Delete("g", runTime);
+        meshObjects::gravity::Delete("#none#", runTime);
+
+        Info<< "after Delete: "
+            << flatOutput(runTime.sortedToc()) << endl;
     }
 
-    meshObjects::gravity::Delete("g", runTime);
-    meshObjects::gravity::Delete("something-not-in-registry", runTime);
 
-    Info<< "after Delete" << nl;
-    Pout<< "registered:" << flatOutput(runTime.sortedToc()) << endl;
+    if (meshObjects::gravity::Store(std::move(release1)))
+    {
+        Info<< "Store pointer" << endl;
+    }
+
+    if (release2)
+    {
+        release2.reset();
+        Info<< "Clear pointer" << endl;
+    }
+
+    Info<< "Before exit: "
+        << flatOutput(runTime.sortedToc()) << endl;
 
     Info<< "\nEnd\n" << endl;
 
diff --git a/src/OpenFOAM/db/regIOobject/regIOobject.H b/src/OpenFOAM/db/regIOobject/regIOobject.H
index 6dad7249ca71f7be7efbf3a4cbebe604594b2ba2..76fbd3ea2ec3c8b7053f93fbf6bfe18f80d074b6 100644
--- a/src/OpenFOAM/db/regIOobject/regIOobject.H
+++ b/src/OpenFOAM/db/regIOobject/regIOobject.H
@@ -33,8 +33,10 @@ Description
 
 SourceFiles
     regIOobject.C
+    regIOobjectI.H
     regIOobjectRead.C
     regIOobjectWrite.C
+    regIOobjectMetaData.C
 
 \*---------------------------------------------------------------------------*/
 
@@ -170,7 +172,7 @@ public:
             //      or was newly registered
             bool checkIn();
 
-            //- Remove all file watches and remove object from registry
+            //- Remove object from registry, and remove all file watches
             //  \return true if object was registered and was removed
             bool checkOut();
 
diff --git a/src/OpenFOAM/meshes/MeshObject/MeshObject.C b/src/OpenFOAM/meshes/MeshObject/MeshObject.C
index 26bbf42e81a33220821171415e71f34b6c652a59..0551457350e3ef58fab1d28969b4889e8eb80ca6 100644
--- a/src/OpenFOAM/meshes/MeshObject/MeshObject.C
+++ b/src/OpenFOAM/meshes/MeshObject/MeshObject.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2023 OpenCFD Ltd.
+    Copyright (C) 2018-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -74,8 +74,8 @@ const Type& Foam::MeshObject<Mesh, MeshObjectType, Type>::New
     if (meshObject::debug)
     {
         Pout<< "MeshObject::New(const " << Mesh::typeName
-            << "&, ...) : constructing " << Type::typeName
-            << " for region " << mesh.name() << endl;
+            << "&, ...) : constructing <" << Type::typeName
+            << ">, region=" << mesh.name() << endl;
     }
 
     ptr = new Type(mesh, std::forward<Args>(args)...);
@@ -108,9 +108,8 @@ const Type& Foam::MeshObject<Mesh, MeshObjectType, Type>::New
     {
         Pout<< "MeshObject::New('" << objName
             << "', const " << Mesh::typeName
-            << "&, ...) : constructing " << objName
-            << " of type " << Type::typeName
-            << " for region " << mesh.name() << endl;
+            << "&, ...) : constructing <" << Type::typeName
+            << ">, region=" << mesh.name() << endl;
     }
 
     ptr = new Type(objName, mesh, std::forward<Args>(args)...);
@@ -138,8 +137,8 @@ bool Foam::MeshObject<Mesh, MeshObjectType, Type>::Delete
     {
         if (meshObject::debug)
         {
-            Pout<< "MeshObject::Delete(const Mesh&) : deleting "
-                << objName << endl;
+            Pout<< "MeshObject::Delete() : deleting <" << Type::typeName
+                << "> " << objName << endl;
         }
 
         return mesh.thisDb().checkOut(static_cast<MeshObjectType<Mesh>*>(ptr));
@@ -149,31 +148,114 @@ bool Foam::MeshObject<Mesh, MeshObjectType, Type>::Delete
 }
 
 
+template<class Mesh, template<class> class MeshObjectType, class Type>
+std::unique_ptr<Type> Foam::MeshObject<Mesh, MeshObjectType, Type>::Release
+(
+    const word& objName,
+    const Mesh& mesh,
+    const bool checkout
+)
+{
+    Type* ptr =
+        mesh.thisDb().objectRegistry::template
+        getObjectPtr<Type>(objName);
+
+    std::unique_ptr<Type> released;
+
+    if (ptr)
+    {
+        auto* casted = static_cast<MeshObjectType<Mesh>*>(ptr);
+
+        if (casted->regIOobject::ownedByRegistry())
+        {
+            // Release ownership from registry and transfer to unique_ptr
+            casted->regIOobject::release();
+            released.reset(ptr);
+
+            // Allow removal from the registry (ie, checkOut) but leave its
+            // 'registered' status untouched since this is equivalent to
+            // IOobject::registerObject().
+            //
+            // Do not use regIOobject::release(unregister) since this
+            // will prevent later re-storing
+
+            if (checkout)
+            {
+                casted->regIOobject::checkOut();
+            }
+        }
+
+        if (meshObject::debug)
+        {
+            Pout<< "MeshObject::Release() : release <" << Type::typeName
+                << "> " << objName << ", owned=" << bool(released) << endl;
+        }
+    }
+
+    return released;
+}
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+template<class Mesh, template<class> class MeshObjectType, class Type>
+bool Foam::MeshObject<Mesh, MeshObjectType, Type>::Store
+(
+    std::unique_ptr<Type>&& ptr
+)
+{
+    bool ok = false;
+
+    if (ptr)
+    {
+        auto* casted = static_cast<MeshObjectType<Mesh>*>(ptr.get());
+
+        ok = casted->regIOobject::store();
+
+        if (ok)
+        {
+            // Took ownership
+            (void) ptr.release();
+        }
+
+        if (meshObject::debug)
+        {
+            Pout<< "MeshObject::Store() : store <" << Type::typeName
+                << ">, owned=" << ok << endl;
+        }
+    }
+
+    return ok;
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
 template<class Mesh>
 void Foam::meshObject::movePoints(objectRegistry& obr)
 {
-    HashTable<GeometricMeshObject<Mesh>*> meshObjects
+    UPtrList<GeometricMeshObject<Mesh>> meshObjects
     (
-        obr.lookupClass<GeometricMeshObject<Mesh>>()
+        obr.sorted<GeometricMeshObject<Mesh>>()
     );
 
     if (meshObject::debug)
     {
-        Pout<< "meshObject::movePoints(objectRegistry&) :"
-            << " moving " << Mesh::typeName
-            << " meshObjects for region " << obr.name() << endl;
+        Pout<< "meshObject::movePoints() : moving "
+            << meshObjects.size() << " <" << Mesh::typeName
+            << "> meshObjects, region=" << obr.name() << endl;
     }
 
-    forAllIters(meshObjects, iter)
+    for (auto& item : meshObjects)
     {
-        // isA<MoveableMeshObject<Mesh>>
-        auto* objectPtr = dynamic_cast<MoveableMeshObject<Mesh>*>(*iter);
+        // isA_constCast<MoveableMeshObject<Mesh>>
+        auto* objectPtr = dynamic_cast<MoveableMeshObject<Mesh>*>(&item);
 
         if (objectPtr)
         {
             if (meshObject::debug)
             {
-                Pout<< "    Moving " << (*iter)->name() << endl;
+                Pout<< "    Moving " << item.name() << endl;
             }
             objectPtr->movePoints();
         }
@@ -181,9 +263,9 @@ void Foam::meshObject::movePoints(objectRegistry& obr)
         {
             if (meshObject::debug)
             {
-                Pout<< "    Destroying " << (*iter)->name() << endl;
+                Pout<< "    Destroying " << item.name() << endl;
             }
-            obr.checkOut(*iter);
+            obr.checkOut(item);
         }
     }
 }
@@ -192,28 +274,28 @@ void Foam::meshObject::movePoints(objectRegistry& obr)
 template<class Mesh>
 void Foam::meshObject::updateMesh(objectRegistry& obr, const mapPolyMesh& mpm)
 {
-    HashTable<GeometricMeshObject<Mesh>*> meshObjects
+    UPtrList<GeometricMeshObject<Mesh>> meshObjects
     (
-        obr.lookupClass<GeometricMeshObject<Mesh>>()
+        obr.sorted<GeometricMeshObject<Mesh>>()
     );
 
     if (meshObject::debug)
     {
-        Pout<< "meshObject::updateMesh(objectRegistry&, "
-               "const mapPolyMesh& mpm) : updating " << Mesh::typeName
-            << " meshObjects for region " << obr.name() << endl;
+        Pout<< "meshObject::updateMesh() : updating "
+            << meshObjects.size() << " <" << Mesh::typeName
+            << "> meshObjects, region=" << obr.name() << endl;
     }
 
-    forAllIters(meshObjects, iter)
+    for (auto& item : meshObjects)
     {
-        // isA<UpdateableMeshObject<Mesh>>
-        auto* objectPtr = dynamic_cast<UpdateableMeshObject<Mesh>*>(*iter);
+        // isA_constCast<UpdateableMeshObject<Mesh>>
+        auto* objectPtr = dynamic_cast<UpdateableMeshObject<Mesh>*>(&item);
 
         if (objectPtr)
         {
             if (meshObject::debug)
             {
-                Pout<< "    Updating " << (*iter)->name() << endl;
+                Pout<< "    Updating " << item.name() << endl;
             }
             objectPtr->updateMesh(mpm);
         }
@@ -221,9 +303,9 @@ void Foam::meshObject::updateMesh(objectRegistry& obr, const mapPolyMesh& mpm)
         {
             if (meshObject::debug)
             {
-                Pout<< "    Destroying " << (*iter)->name() << endl;
+                Pout<< "    Destroying " << item.name() << endl;
             }
-            obr.checkOut(*iter);
+            obr.checkOut(item);
         }
     }
 }
@@ -232,25 +314,25 @@ void Foam::meshObject::updateMesh(objectRegistry& obr, const mapPolyMesh& mpm)
 template<class Mesh, template<class> class MeshObjectType>
 void Foam::meshObject::clear(objectRegistry& obr)
 {
-    HashTable<MeshObjectType<Mesh>*> meshObjects
+    UPtrList<MeshObjectType<Mesh>> meshObjects
     (
-        obr.lookupClass<MeshObjectType<Mesh>>()
+        obr.sorted<MeshObjectType<Mesh>>()
     );
 
     if (meshObject::debug)
     {
-        Pout<< "meshObject::clear(objectRegistry&) :"
-            << " clearing " << Mesh::typeName
-            << " meshObjects for region " << obr.name() << endl;
+        Pout<< "meshObject::clear() : clearing "
+            << meshObjects.size() << " <" << Mesh::typeName
+            << "> meshObjects, region=" << obr.name() << endl;
     }
 
-    forAllIters(meshObjects, iter)
+    for (auto& item : meshObjects)
     {
         if (meshObject::debug)
         {
-            Pout<< "    Destroying " << (*iter)->name() << endl;
+            Pout<< "    Destroying " << item.name() << endl;
         }
-        obr.checkOut(*iter);
+        obr.checkOut(item);
     }
 }
 
@@ -263,30 +345,30 @@ template
 >
 void Foam::meshObject::clearUpto(objectRegistry& obr)
 {
-    HashTable<FromType<Mesh>*> meshObjects
+    UPtrList<FromType<Mesh>> meshObjects
     (
-        obr.lookupClass<FromType<Mesh>>()
+        obr.sorted<FromType<Mesh>>()
     );
 
     if (meshObject::debug)
     {
-        Pout<< "meshObject::clearUpto(objectRegistry&) :"
-            << " clearing " << Mesh::typeName
-            << " meshObjects for region " << obr.name() << endl;
+        Pout<< "meshObject::clearUpto() : clearing "
+            << meshObjects.size() << " <" << Mesh::typeName
+            << "> meshObjects, region=" << obr.name() << endl;
     }
 
-    forAllIters(meshObjects, iter)
+    for (auto& item : meshObjects)
     {
-        // isA<ToType<Mesh>>
-        auto* objectPtr = dynamic_cast<ToType<Mesh>*>(*iter);
+        // isA_constCast<ToType<Mesh>>
+        auto* objectPtr = dynamic_cast<ToType<Mesh>*>(&item);
 
         if (!objectPtr)
         {
             if (meshObject::debug)
             {
-                Pout<< "    Destroying " << (*iter)->name() << endl;
+                Pout<< "    Destroying " << item.name() << endl;
             }
-            obr.checkOut(*iter);
+            obr.checkOut(item);
         }
     }
 }
diff --git a/src/OpenFOAM/meshes/MeshObject/MeshObject.H b/src/OpenFOAM/meshes/MeshObject/MeshObject.H
index 11554ee3dac86bd40a6546bffa2014678d6988b3..a5d0577ade925e35af888515c04c54f073a247c5 100644
--- a/src/OpenFOAM/meshes/MeshObject/MeshObject.H
+++ b/src/OpenFOAM/meshes/MeshObject/MeshObject.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2018-2023 OpenCFD Ltd.
+    Copyright (C) 2018-2024 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -34,8 +34,9 @@ Description
     MeshObject is templated on the type of mesh it is allocated to, the type of
     the mesh object (TopologicalMeshObject, GeometricMeshObject,
     MoveableMeshObject, UpdateableMeshObject) and the type of the actual object
-    it is created for example:
+    it is created for.
 
+    Example usage,
     \verbatim
     class leastSquaresVectors
     :
@@ -49,13 +50,15 @@ Description
     };
     \endverbatim
 
-    MeshObject types:
-
-    - TopologicalMeshObject: mesh object to be deleted on topology change
-    - GeometricMeshObject: mesh object to be deleted on geometry change
-    - MoveableMeshObject: mesh object to be updated in movePoints
-    - UpdateableMeshObject: mesh object to be updated in updateMesh or
-        movePoints
+    The MeshObject types:
+    - TopologicalMeshObject:
+      mesh object to be deleted on topology change
+    - GeometricMeshObject:
+      mesh object to be deleted on geometry change
+    - MoveableMeshObject:
+      mesh object to be updated in movePoints
+    - UpdateableMeshObject:
+      mesh object to be updated in movePoints or updateMesh
 
 Note
     movePoints must be provided for MeshObjects of type MoveableMeshObject
@@ -82,6 +85,55 @@ namespace Foam
 // Forward Declarations
 class mapPolyMesh;
 
+/*---------------------------------------------------------------------------*\
+                           Class meshObject Declaration
+\*---------------------------------------------------------------------------*/
+
+//- The meshObject is a concrete regIOobject to register MeshObject items
+class meshObject
+:
+    public regIOobject
+{
+public:
+
+    //- Runtime declaration and debug switch
+    ClassName("meshObject");
+
+
+    // Constructors
+
+        //- Construct with given object name on a registry
+        meshObject(const word& objName, const objectRegistry& obr);
+
+
+    // Static Member Functions
+
+        //- Update for mesh motion
+        template<class Mesh>
+        static void movePoints(objectRegistry& obr);
+
+        //- Update topology using the given map
+        template<class Mesh>
+        static void updateMesh(objectRegistry& obr, const mapPolyMesh& mpm);
+
+        //- Clear/remove all meshObject of MeshObjectType
+        //- via objectRegistry::checkOut()
+        template<class Mesh, template<class> class MeshObjectType>
+        static void clear(objectRegistry& obr);
+
+        //- Clear all meshObject derived from FromType up to
+        //- (but not including) ToType.
+        //  Used to clear e.g. all non-updateable meshObjects
+        template
+        <
+            class Mesh,
+            template<class> class FromType,
+            template<class> class ToType
+        >
+        static void clearUpto(objectRegistry& obr);
+};
+
+
 /*---------------------------------------------------------------------------*\
                          Class MeshObject Declaration
 \*---------------------------------------------------------------------------*/
@@ -108,14 +160,17 @@ public:
         MeshObject(const word& objName, const Mesh& mesh);
 
 
+    //- Destructor
+    virtual ~MeshObject() = default;
+
+
     // Factory Methods
 
-        //- Get existing or create a new MeshObject. Registered with typeName
+        //- Get existing or create MeshObject registered with typeName
         template<class... Args>
         static const Type& New(const Mesh& mesh, Args&&... args);
 
-        //- Get existing or create a new MeshObject using supplied
-        //- registration name
+        //- Get existing or create MeshObject with given registration name
         template<class... Args>
         static const Type& New
         (
@@ -124,11 +179,10 @@ public:
             Args&&... args
         );
 
+        //- Transfer ownership of meshObject to registry.
+        static bool Store(std::unique_ptr<Type>&& ptr);
 
-    //- Destructor
-    virtual ~MeshObject() = default;
-
-        //- Static destructor using supplied registration name
+        //- Static destructor using given registration name
         static bool Delete(const word& objName, const Mesh& mesh);
 
         //- Static destructor using Type::typeName
@@ -137,6 +191,29 @@ public:
             return Delete(Type::typeName, mesh);
         }
 
+        //- Release ownership of meshObject (with given registration name)
+        //- from registry. Returns nullptr if not found or not owned.
+        static std::unique_ptr<Type> Release
+        (
+            const word& objName,
+            const Mesh& mesh,
+            //! optionally perform checkOut() from the registry
+            const bool checkout = false
+        );
+
+
+        //- Release ownership of meshObject (with Type::typeName name)
+        //- from registry.
+        static std::unique_ptr<Type> Release
+        (
+            const Mesh& mesh,
+            //! optionally perform checkOut() from the registry
+            const bool checkout = false
+        )
+        {
+            return Release(Type::typeName, mesh, checkout);
+        }
+
 
     // Member Functions
 
@@ -154,51 +231,6 @@ public:
 };
 
 
-/*---------------------------------------------------------------------------*\
-                           Class meshObject Declaration
-\*---------------------------------------------------------------------------*/
-
-//- The meshObject is a concrete regIOobject
-class meshObject
-:
-    public regIOobject
-{
-public:
-
-    //- Runtime declaration and debug switch
-    ClassName("meshObject");
-
-
-    // Constructors
-
-        //- Construct with given object name on a registry
-        meshObject(const word& objName, const objectRegistry& obr);
-
-
-    // Static Member Functions
-
-        template<class Mesh>
-        static void movePoints(objectRegistry& obr);
-
-        template<class Mesh>
-        static void updateMesh(objectRegistry& obr, const mapPolyMesh& mpm);
-
-        template<class Mesh, template<class> class MeshObjectType>
-        static void clear(objectRegistry& obr);
-
-        //- Clear all meshObject derived from FromType up to
-        //- (but not including) ToType.
-        //  Used to clear e.g. all non-updateable meshObjects
-        template
-        <
-            class Mesh,
-            template<class> class FromType,
-            template<class> class ToType
-        >
-        static void clearUpto(objectRegistry& obr);
-};
-
-
 /*---------------------------------------------------------------------------*\
                     Class TopologicalMeshObject Declaration
 \*---------------------------------------------------------------------------*/
@@ -254,6 +286,7 @@ public:
         GeometricMeshObject<Mesh>(objName, obr)
     {}
 
+    //- Update for mesh motion
     virtual bool movePoints() = 0;
 };
 
@@ -275,6 +308,7 @@ public:
         MoveableMeshObject<Mesh>(objName, obr)
     {}
 
+    //- Update topology using the given map
     virtual void updateMesh(const mapPolyMesh& mpm) = 0;
 };