4

Goal: Reduce memory footprint

My approach is to create single realm instance in AppDelegate class & then access that instead of creating a new variable each time.

AppDelegate

lazy var realm: Realm = {
    let realm = try! Realm()
    // Get our Realm file's parent directory
    if let folderPath = realm.configuration.fileURL?.URLByDeletingLastPathComponent?.path{
        // Disable file protection for this directory
        do {
            try NSFileManager.defaultManager().setAttributes([NSFileProtectionKey: NSFileProtectionNone],ofItemAtPath: folderPath)
        }catch {
            printDebug(error)
        }
    }
    return realm
}()

UIViewController

var realm = (UIApplication.sharedApplication().delegate as! AppDelegate).realm

// Access/Modify realm object
try! self.realm.write{
     location.imageFile = fileName
}

Questions

1. Will this help reduce memory usage?

2. What are the drawbacks?

harsh_v
  • 3,193
  • 3
  • 34
  • 53

2 Answers2

2

Interesting question

1. In my opinion major drawback is that if you're going to use GCD with Realm. Remember that Realm is thread-safe so you can't use/modify Realm Object across threads/queues.

I handle Realm with Manager which is singleton. Maybe someone has better solution but this works just great.

class CouponManager: NSObject {
        /// path for realm file 
    lazy private var realmURL: NSURL = {
        let documentUrl = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask)[0]
        let url = documentUrl.URLByAppendingPathComponent("coupons.realm")
        return url
    }()
    lazy private var config:Realm.Configuration = {
        return Realm.Configuration(
            fileURL: self.realmURL,
            inMemoryIdentifier: nil,
            encryptionKey: "my65bitkey".dataUsingEncoding(NSUTF8StringEncoding),
            readOnly: false,
            schemaVersion: 1,
            migrationBlock: nil,
            deleteRealmIfMigrationNeeded: false,
            objectTypes: nil)
    }()

    static let shared: CouponManager = CouponManager()

    func save(coupons coupons:[Coupon]) {
        let realm = try! Realm(configuration: config)
        try! realm.write(){
            realm.deleteAll()
            realm.add(coupons)
        }
    }

    func load() -> Results<Coupon> {
        let realm = try! Realm(configuration: config)
        return realm.objects(Coupon)
    }

    func deleteAll() {
        let realm = try! Realm(configuration: config)
        try! realm.write({ 
            realm.deleteAll()
        })
    }
}

2. You shouldn't worry about memory when use Realm. As TiM (he works in Realm) said in this answer and you should instantiate Realm every time you need to.

Community
  • 1
  • 1
Błażej
  • 3,617
  • 7
  • 35
  • 62
  • I've already read that answer & It's quite useful. I was concerned about the thread safety with my approach. Creating a realm instance each time avoids any errors on the other hand creating one global instance keeps my code clean. I think creating a global function instead of global variable will be better. I can avoid calling realm from wrong thread & it will also keep the code dry – harsh_v Aug 04 '16 at 07:06
  • So that why I have this manager and that why in each method I create instance of realm. It look clean but some may said that singleton is the worst solution. – Błażej Aug 04 '16 at 07:10
  • I only use singleton when it is ultimately necessary. Also, as I am using default realm configuration, I don't think I need to create singleton but your method seems nice. It gave me the idea to use global function. :) Thanks – harsh_v Aug 04 '16 at 07:14
  • @blazej do you still have the same experience with Realm. using Realm Singleton in all methods that require realm....? with no mem issues. – sirvon Jan 31 '18 at 19:09
  • @sirvon no, it's a pain to test such code. Now each time something is needed to be done, then I create a new instance of `Realm`. Sometimes a configuration is a var, sometimes can be passed as a parameter. – Błażej Jan 31 '18 at 22:43
2

Questions

  1. Will this help reduce memory usage?

From my experience, getting the shared instance of realm and setting the URL is not an heavy task. Yet opening and closing a transaction some times is (depends on how often you do that, but at the end of the day most clients do not write heavily enough). So my answer for that is no, this will not help in memory usage. And by the way, did you profile your app to check were does your memory usage go to? Thats a good place to start at.

  1. What are the drawbacks?

I have been using realm for some time now, and I've written 2 apps in production with Realm . Now what I'm about to say is not about memory usage, but about design.

A. Realm as you know is a data base. And you should not just access it from any random place in the application (not from a viewController), especially without wapping it with some class of yours (UsersDataBase class for example), and I'll explain. When objects in the DB start changing, you need to know who, were and from witch thread the writing is to DB. One place were you can check and debug. And when I'ts all over your application I't is very hard to debug.

B. Realm is not Thread safe. That means that you really want to know on witch thread realm is getting written too. If not, you will end up getting thread executions, and believe be, this is not a fun thing to deal with. Especily when realm does not help with the stack trace some times.

C. You are accessing realm on AppDelegate with "UIApplication.sharedApplication()". This is not best practice since you can't access "UIApplication.sharedApplication()" from diffrent contexts, for example App Extensions. And when you will want to add App extensions to your app, you will end up reWriting any place your using it.

MCMatan
  • 8,623
  • 6
  • 46
  • 85
  • Thanks for a detailed answer. I liked that part "you can't access "UIApplication.sharedApplication()" from diffrent contexts". Didn't thought about that earlier. And also it is hard to find bugs when using realm. – harsh_v Aug 04 '16 at 08:19
  • I used instruments to check for memory leaks. Found that realm initialization creates a memory leak. @MCMatan – harsh_v Aug 05 '16 at 06:31
  • Can you be more specific? Realm is used in many production applications, and I can't see any issue opened saying it is courses a memorie leak (Do you mean memorie leak, or memorie usage?).. It sounds like you are doing something that courses it, for instance maybe you are going some action from a block without using a "weak self" reference. – MCMatan Aug 05 '16 at 08:02
  • https://github.com/realm/realm-cocoa/issues/2933 This issue seems old but this is exactly what encounter when I use `let realm = try! Realm()`, first time in application – harsh_v Aug 05 '16 at 08:38
  • Or sorry about not noticing the issue before, very interesting, does it leak every time you call " let realm = try! Realm()", or just the first time? – MCMatan Aug 06 '16 at 10:49
  • Only for the very first initialization of realm – harsh_v Aug 06 '16 at 16:46
  • Nope. I've decided to go with the realm documentation. Create realm instance every time. I refracted the code to use global function. That provides me a little cleaner solution & I use `lazy var ` whenever possible. – harsh_v Aug 06 '16 at 17:17