2

I have read Where to put iVars in "modern" Objective-C? and some other questions, but I am still confused.

I am reading from https://www.raywenderlich.com/913/sqlite-tutorial-for-ios-making-our-app SQLite Tutorial:

.h

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface FailedBankDatabase : NSObject {
sqlite3 *_database; 
}

+ (FailedBankDatabase*)database;
- (NSArray *)failedBankInfos;

@end

.m

#import "FailedBankDatabase.h"
#import "FailedBankInfo.h"

@implementation FailedBankDatabase

static FailedBankDatabase *_database;

+ (FailedBankDatabase*)database {
    if (_database == nil) {
        _database = [[FailedBankDatabase alloc] init];
    }
    return _database;
}

Please correct where I am wrong: The sqlite3 * database:

  • Isn't a property
  • Doesn't have any synthesize
  • Doesn't have any setter or getter in .m file so no other class can access it--defeating the purpose of placing in .h file

All meaning we can't access nor set it! Why are we doing this?

What what is the purpose of placing this in .h file; Why not just write as a property? Or just write in our .m file alone?

Edit: What is the modern way of using Sqlite3 in Objective-C?

Community
  • 1
  • 1
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • Why is there an instance variable called `_database` as well as a global variable called `_database`? That cannot be right. – trojanfoe Apr 12 '16 at 14:49
  • it's certainly confusing, and bad form, particularly for a tutorial... – Wain Apr 12 '16 at 14:49
  • @trojanfoe What's very helpful? So what's the correct approach? What should be done if the instance variable should not be there? Would you ever write an instance variable inside .h without writing a getter and setter for it? – mfaani Apr 12 '16 at 14:54
  • @asma22 All the time, but this example is particularly bad. See my answer. – trojanfoe Apr 12 '16 at 14:55
  • see [this](http://stackoverflow.com/a/658751/5175709) answer for knowing how to access ivars written in .h from another class; You must use `->` to access an object/instance's variable `obj->var` – mfaani Apr 21 '16 at 14:22

2 Answers2

2

You're part right, sqlite3 *_database isn't a property, has no synthesised setter or getter, but you can access it - by direct reference. If it's in the header it's publicly declared so anyone with an instance of FailedBankDatabase can directly access the memory location of _database and interact with it. It just doesn't call a getter to do it.

Because of this using properties is better. By asking clients to go through the getter you have more control over access.

This static FailedBankDatabase *_database; is a same name but completely unrelated variable. It's static so it has class scope and the scope of the method which is accessing _database determines which variable is being accessed. This is confusing and bad form...

Wain
  • 118,658
  • 15
  • 128
  • 151
  • Confused I am! What do you mean by direct reference? Can I say `FailedBankDatabase._database` similar to what I would do had it been a property? If it doesn't call using a getter then what does it call!? – mfaani Apr 12 '16 at 15:01
  • no, because that's a class, you'd direct reference in an instance (but you generally don't want to do that). – Wain Apr 12 '16 at 16:46
  • Thank you so much! Oh Yes I meant direct reference an instance. So is there any use case for having ivars in .h--if you saw we generally don't want to do that. And what are the defaults for ivars? Are they same as atomic, strong, readwrite? – mfaani Apr 12 '16 at 16:52
  • 1
    There isn't a good use case, it's all cheating. There is no spec about access. That's why properties exist... – Wain Apr 12 '16 at 19:23
  • Can you please refractor it with using property ? – mfaani Apr 23 '16 at 14:26
2

It was not always possible to declare instance variables in the implementation block. It has been possible for several years, but the date of that tutorial is April 8, 2010. When it was written, the author probably wanted it to work on older versions of the compiler.

Because _database is not declared @private, it is in fact possible to access the instance variable outside of the FailedBankDatabase implementation. In Objective-C, you can treat an object pointer as a struct pointer and access its instance variables like struct fields. So you could do this:

#import "FailedBankDatabase.h"

int main(int argc, char *argv[])
{
    FailedBankDatabase *db = [FailedBankDatabase database];
    printf("instance variable: %p\n", db->_database);
    return 0;
}

That's probably a bad idea, though.

UPDATE

Since your sqlite3 *_database is intended to be private, you probably just want to do this:

FailedBankDatabase.h

#import <Foundation/Foundation.h>

@class FailedBankInfo;

@interface FailedBankDatabase : NSObject

+ (FailedBankDatabase*)database;
- (NSArray<FailedBankInfo *> *)failedBankInfos;

@end

FailedBankDatabase.m

#import "FailedBankDatabase.h"
#import "FailedBankInfo.h"
#import <sqlite3.h>

@implementation FailedBankDatabase {
    sqlite3 *_database;
}

static FailedBankDatabase *theInstance;

+ (FailedBankDatabase*)database {
    if (theInstance == nil) {
        theInstance = [[FailedBankDatabase alloc] init];
    }
    return theInstance;
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • For newbies like myself: Since when we are dealing with instances of classes, we are working a pointer to an object (special struct). Thus, we access public fields using `->`. If it was a property we would have accessed using dot notation. – mfaani Apr 12 '16 at 17:31
  • What is the modern Objective-C way of doing this? shall I make sqlite3 as my property? Kindly can you edit your answer to include the modern way of doing it? – mfaani Apr 21 '16 at 14:11
  • Rob, Thanks for the update. Did you just add `` as a nicety so that ensure all objects in the array of that type ? Also don't we need to use dispatch_once so that we protect ourselves from a race condition? i.e. avoid 2 instances of our shared instance to be created? – mfaani Apr 23 '16 at 15:22
  • 1
    Yes, I added that for better type checking. You only need to add `dispatch_once` if you want to allow `FailedBankDatabase` to be used from multiple threads, but then you probably also need locks in many other places. – rob mayoff Apr 23 '16 at 15:57