lundgren
lundgren

Reputation: 55

Use a std::shared_ptr as a global variable in Objective-C

I'm trying to implement a class from my c++ .h-file together with shared_ptr:s.

Why doesn't my shared_ptr (totPtr) keep its value between the methods?

Example code when I've use a double instead of the class:

#include <memory>

static std::shared_ptr<myCppClass> totPtr;
static double tot; 


@implementation ViewController


- (void) createSelf
{
    totPtr = std::make_shared<myCppClass>(5); 
    tot = 10;   
}

This method is called after createSelf:

- (void)otherMethod 
{
    tot += 1; // works, tot = 11
    totPtr->doStuff(); // doesn't work, totPtr = nullptr 

}

tot2 still has has the value = 0, but tot has the value = 10

Upvotes: 1

Views: 1622

Answers (2)

Bryan Chen
Bryan Chen

Reputation: 46598

For global variables, you should initialized it in +[initialize], which is a thread-safe way to initialize any global variables

+ (void)initialize {
    if (self == [ViewController self]) { // need to guard it so not get called again from subclass
        totPtr = std::make_shared<MyClass>();
        tot = 10;
    }
}

If you initialize them in viewDidLoad, it may works now, but when you decide to have multiple instance of ViewController, you will find the second call of viewDidLoad will reset these gloabl variables.

BUT normally you don't want global variable, you want instance variable / property

@interface ViewController ()
@property (nonatomic) double tot;
@end

@implementation ViewController {
    std::shared_ptr<myCppClass> _totPtr;
}

which you can initailize them in viewDidLoad which described in the other answer.

Note: I don't recommended to use ObjC property with C++ class (i.e. shared_ptr). Because everytime you access it via property, it require overhead of call a setter/getter method which may need to copy shared_ptr, which is an expansive operation (you should always pass shared_ptr by reference in method argument, but this doesn't have good support in ObjC). Access ivar directly allow you to avoid the overhead.

Upvotes: 2

Holinov Ilya
Holinov Ilya

Reputation: 33

It is better not to use global variables - better to use properties.

But if you still need it - don't use init method for it.

On yours code i don't see who is calling createSelf method.

Here i show working code.

#import "ViewController.h"
#import "MyClass.h"
#include <memory>

static std::shared_ptr<MyClass> totPtr;
static double tot;

@interface ViewController ()
@property (nonatomic) std::shared_ptr<MyClass> p1;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self m1];
    [self m2];
    // Do any additional setup after loading the view, typically from a nib.
}

-(void) m1{
    totPtr = std::make_shared<MyClass>();
    self.p1 = std::make_shared<MyClass>();
    tot = 10;
}

-(void) m2{
    NSLog(@"id: %d",self.p1->id());
    NSLog(@"id: %d",totPtr->id());
}

- (IBAction)ev1:(id)sender {
    [self m2];
}

@end

Update:

You could use this method if you want to init that var in init* method

- (instancetype)initWithCoder:(NSCoder *)coder* 

Upvotes: 1

Related Questions