Im trying to create a plugin for unity game on ios platform
For some reason the second time i try to access the static property shared instance i get
Thread 1: EXC_BAD_ACCESS (code=1, address=0xb21c290b0) on line => auto *instance = [[AppattestPluginWrapper sharedInstance] appAttestPlugin];
Weird thing is when i use break point in the sharedInstance property,the value returned isn't nil so it looks like something in the assignment of the var *instance crashes and i can't figure out why.
#import "UnityAppController.h"
#import <Foundation/Foundation.h>
#import "UnityFramework/UnityFramework-Swift.h"
#interface AppattestPluginWrapper: NSObject
{
}
#property (nonatomic, strong) AppAttestPlugin *appAttestPlugin;
#end
#implementation AppattestPluginWrapper
+ (id)sharedInstance {
static AppattestPluginWrapper *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
sharedInstance.appAttestPlugin = [[AppAttestPlugin alloc] init];
});
return sharedInstance;
}
#end
extern "C"{
void _generateAppAttestKeyId(){
auto *instance = [[AppattestPluginWrapper sharedInstance] appAttestPlugin];
[instance generateAppAttestKeyWithCompletion: ^(NSString * response, NSError * error){
if(error){
UnitySendMessage("iOSListener", "OnAppAttestKeyGenerationFailed", [[error localizedDescription] UTF8String]);
}
else if(response){
UnitySendMessage("iOSListener","OnAppAttestKeyGenerated", [response UTF8String]);
}
}];
}
}
The cause for this behaviour was a crash that corrupted the shared instance, for some reason there was no indicator for the corruption and it took some time to detect the issue that was related to the unity code.
Related
This question already has an answer here:
What is the use of Singleton class in objective-c? [duplicate]
(1 answer)
Closed 7 years ago.
I am new to iOS development and I have gone through singleton class. I understood the concept, but having doubts in implementing the singleton class. Can anyone please share source code of the real time example using singleton class.
This is how a GCD for singleton class looks like.
Suppose there is a class that you made, MySingleTonClass which is a subclass of NSObject
MySingleTonClass.h
+(instanceType)sharedManager;
#property (nonatomic, strong) NSString *userName;
MySingleTonClass.m
+(instanceType)sharedManager{
static MySingleTonClass *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[MySingleTonClass alloc]init];
});
return manager;
}
Now you call this singleTon Class in some other class suppose in ViewController.m. First Import the Class
#import MySingleTonClass.h
-(void)viewDidLoad{
MySingleTonClass *manager = [MySingleTonClass sharedManager];
manager.userName = #"ABCDE";
//manager is the singleton Object
}
Edit
Now suppose you want to access this same value. then suppose in some other ViewController, after ViewController
Suppose in SecondViewController.m
#import "MySingleTonClass.h"
-(void)viewDidLoad{
MySingleTonClass *manager = [MySingleTonClass sharedManager];
NSLog (#"%#",manager.userName);
// This would still log ABCDE, coz you assigned it the class before, So even if you create a new object called manager here, it will return the same Manager you created before.
manager.userName = #"Myname"; //Now the value changed to MyName untill you change it again, in the lifetime of this application.
}
I hope i could make you understand the concept of it.
As you know, dispatch_once_t is a GCD snippet that makes the code inside of it invoke only ONCE per application run. Any code you write inside it will be run, or rather invoked only once in the lifetime of the application being active.
Check out this link for the original source - http://getsetgames.com/2009/08/30/the-objective-c-singleton/
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
static User *defaultUser;
+ (User *)defaultUser
{
if (!defaultUser)
{
defaultUser = [self new];
// do something...
}
return defaultUser;
}
There are two ways:-
1) We can create singleton class using **GCD** dispatch_once
in this only one object will create if existing object is there then it will refer to them.
+(id)sharedManager
{
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
2) Second way is follows:-
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
suppose this above method is written in class **MyManager** then u can use that as follow
MyManager *sharedManager = [MyManager sharedManager];
hope this will help u.
Ok, strange thing occurred and I guess answer is quite simple, but I fail to figure out what's going on.
Situation is next:
I have an NSObject class called Constants.
Constants.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <GooglePlus/GooglePlus.h>
#interface Constants : NSObject
+(Constants*)shared;
#property GTLPlusPerson* googlePlusUser;
#property int profileType;
#property NSString *userName, *userLastName, *userEmail, *userGoogleId,*userProfilePicture;
#end
Constants.m
#import "Constants.h"
#implementation Constants
#synthesize profileType, userProfilePicture, userLastName,userName,userGoogleId,userEmail;
static Constants *constants = nil;
+ (Constants*)shared {
if (nil == constants) {
constants = [[Constants alloc] init];
}
return constants;
}
I use this class in order to save some static variables that I will use throughout the app.
Now, If I try and declare one of the variables like
[Constants shared].userName = #"name";
from an NSObject class method (Which I call from a ViewController), I fail to do so.
But If I declare Constant variables directly from ViewController (after viewDidLoad for example) everything works fine.
Here is the Class I try to declare variables from, but I fail (It also has singleton in it, that might be the source of the problem, but im not sure why would it)
#implementation GoogleLogin
static GoogleLogin* gLogin = nil;
+(GoogleLogin*)shared
{
if (nil == gLogin){
gLogin = [[[self class]alloc]init];
}
return gLogin;
}
-(void)getProfile
{
GTLServicePlus* plusService = [[GTLServicePlus alloc] init];
plusService.retryEnabled = YES;
[plusService setAuthorizer:[GPPSignIn sharedInstance].authentication];
GTLQueryPlus *query = [GTLQueryPlus queryForPeopleGetWithUserId:#"me"];
plusService.apiVersion=#"v1";
[plusService executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLPlusPerson *person,
NSError *error) {
if (error){
NSLog(#"Error while fetching user profile: %#", error);
}else{
NSLog(#"User profile information fetched OK");
[Constants shared].googlePlusUser = person;
[Constants shared].profileType = 1;
[Constants shared].userName = person.name.givenName;
[Constants shared].userLastName = person.name.familyName;
[Constants shared].userEmail = [GPPSignIn sharedInstance].authentication.userEmail;
[Constants shared].userGoogleId = person.identifier;
[Constants shared].userProfilePicture = person.image.url;
NSLog(#"%# %# %# %# %# ",person.name.givenName,person.name.familyName,[GPPSignIn sharedInstance].authentication.userEmail,person.identifier,person.image.url);
}
}];
}
and this is how I call those methods, from my ViewController:
- (IBAction)signupWithGoogle:(UIButton *)sender {
//if i call this method here, on button click, it will finish all the steps needed, except setting constant variables
[[GoogleLogin shared] googleLoginFromViewController:self];
//if I uncomment next line, username will be declared and I will be able to access it later
//[Constants shared].userName = #"Petar";
}
Can anybody figure out why is this happening and what should I do to change that?
When you define a property is strongly suggested to declare the attributes to use with it. I guess the compiler should complain about this with a message like
No 'assign', 'retain', or 'copy' attribute is specified - 'assign' is
assumed
So, use the following instead (copy semantics is fine for mutable classes).
#property (nonatomic, copy) NSString *myString;
You should also specify if the property should be accessed in a atomic or nonatomic way. If you don't specify it, the former will be applied.
Then, you are using a singleton pattern. The suggested way is to use GCD like so.
+ (ConstantsManager*)sharedManager {
static ConstantsManager *sharedManager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[[self class] alloc] init];
});
return sharedManager;
}
Well you did not set your property attributes on the singleton class.
For example,
#property (nonatomic, strong, readonly) ...
Have you tried moving the property assignments out of the completionHandler? It may be that your properties are being assigned on a background thread and your view controller is not catching the assignment. An easy way to check is to override the setters and getters and put breakpoints in them to see what order they are being accessed.
1) Remove the #synthesize because it's not needed (properties will be synthesized as _property automatically)
2) Override setter & getter
-(void)setProfileType:(NSInteger)profileType {
_profileType = profileType;
}
-(NSInteger)profileType {
return _profileType;
}
3) Place breakpoints within these methods and see if the getter is being called before the setter. Alternatively, if simply moving the assignments out of the completionHandler fixes it you know you have some concurrency issues.
I suggest reading up on atomic/nonatomic properties, #synthesize and Objective-C concurrency.
I am converting a project to an SDK. I need to convert several instance methods to class methods. I am getting a compiler warning about using "self". The warning is "Incompatible pointer types initializing Store* with an expression of Class. This Store class is a singleton sharedInstance.
I have method like this in my class Store:
+ (void) dispatchStoreSource {
__weak Store *ref = self; <--- issue is here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSArray *things = [ref fetchThings:&error];
//dispatch back to main queue
if (![ref updateSource:source forUser:user error:&error]) {
dispatch_async(dispatch_get_main_queue(), ^{
result(nil, error);
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
result(source, nil);
});
}
});
}
What is the proper way to fix this? Should it I do this?
__weak Store *ref = [Store sharedInstance];
Your ref is pointer to an object of Store class. But self in your class method doesn't point to an allocated object of your class (= your singleton), it's your Class, IOW Store (not object, but Class). If you have implemented sharedInstance class method, like this ...
+ (instancetype)sharedInstance {
static Story *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
... just do this ref = [self sharedInstance];
Yes, You should go with __weak Store *ref = [Store sharedInstance];
Otherwise Let's use your original static reference of Store.
Example :
static Store = _store = nil;
__weak Store * ref = _store;
hi i am new to ios dev ,I am trying to set a value for nsstring from delegate class and access from other class ,the value i get is null.i dont know what mistake i am doing?
//token class header file
#interface TokenClass : NSObject
{
NSString *tokenValue;
}
#property (nonatomic,strong) NSString *tokenValue;
//token class main file
#implementation TokenClass
#synthesize tokenValue;
#end
//App Delegate
TokenClass *token = [[TokenClass alloc]init];
[token setTokenValue:#"as"];
when i access tokenvalue in some other classs i get null value.
can any one point me what mistake i am doing?Am i using # property correctly?
There are a lot of ways to achieve what you want:
1. Usually I am using NSUserDefaults to save small amount of data which I will need even the user closed the app. There are a lot of information how to use it. See my answer here.
2. In your UIViewController class (e.x. your rootViewController) create #property which will hold your TokenClass. Then you will get tokenValue by self.tokenClass.tokenValue
3. The other way is create a singleton class which will be available during the whole run loop of your application. A Singleton candidate must satisfy three requirements:
controls concurrent access to a shared resource.
access to the resource will be requested from multiple, disparate
parts of the system.
there can be only one object.
+(TokenClass*) sharedTokenClass {
static dispatch_once_t pred;
static TokenClass *_sharedTokenClass = nil;
dispatch_once(&pred, ^{
_sharedTokenClass = [[TokenClass alloc] init];
});
return _sharedTokenClass;
}
You will use it it from any place you want by
[TokenClass sharedTokenClass]tokenValue];
If I were you, I would use the first variant.
PS. I strongly recommend you to read some memory management articles to get the point of object's lifecycle.
You need to use Singleton class to expose variables or objects to the entire project or create global variables. Create sharedInstance of TokenClass class and create property which can be accessed anywhere
in your .h file
//token class header file
#interface TokenClass : NSObject
#property (nonatomic,strong) NSString *tokenValue;
//create static method
+ (id)sharedInstance;
in .m file
#import "TokenClass.h"
#implementation TokenClass
#pragma mark Singleton Methods
+ (id)sharedInstance {
static TokenClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
tokenValue = [[NSString alloc] init];
}
return self;
}
#end
now in your appDelegate
#import TokenClass.h
#implementation AppDelegate
in `didFinishLaunchingWithOptions:`
[TokenClass sharedInstance] setTokenValue:#"as"];
in any class you can get value using
NSLog(#"tokenValue = %#", [[SingletonClass sharedInstance] tokenValue]);
I have a problem with an singleton pattern.
I have read the following tutorials about singleton classes and have created my own.
http://www.galloway.me.uk/utorials/singleton-classes/
http://www.johnwordsworth.com/2010/04/iphone-code-snippet-the-singleton-pattern/
The first time i build & run the app it works like it should. No problems at all!
But when i rebuild the app the singleton class does not work properly anymore. The first init works like it should but when i call it again after a button click it crashes my app.
My singleton class:
BPManager.h
#interface BPManager : NSObject {
NSString *dbPath;
}
#property (nonatomic, retain) NSString *dbPath;
+ (id)bpManager;
- (void)initDatabase:(NSString *)dbName;
- (int)getQuestions;
#end
BPManager.m
static BPManager *sharedMyManager = nil;
#implementation BPManager
#synthesize dbPath;
- (void)initDatabase:(NSString *)dbName
{
dbPath = dbName;
}
-(int)getQuestions
{
NSLog(#"getQuestions");
}
- (id)init {
if ((self = [super init])) {
}
return self;
}
+ (BPManager *) bpManager {
#synchronized(self) {
if(sharedMyManager != nil) return sharedMyManager;
static dispatch_once_t pred; // Lock
dispatch_once(&pred, ^{ // This code is called at most once per app
sharedMyManager = [[BPManager alloc] init];
});
}
return sharedMyManager;
}
- (void)dealloc {
[dbPath release];
[super dealloc];
}
When i call the following code when building my interface, the app creates the singleton:
BPManager *manager = [BPManager bpManager];
[manager initDatabase:#"database.db"];
Note: At this point i can create references to the class from other files as well. But when i click on a button it seems to loose his references.
But when a button is clicked, the following code is ecexuted:
BPManager *manager = [BPManager bpManager];
int count = [manager getQuestions];
The app should get the sharedInstance. That works, only the parameters (like dbPath) are not accessible. Why is that?
Edit:
after some research, i have changed the method to:
+ (BPManager *) bpManager {
#synchronized(self) {
if(sharedMyManager != nil) return sharedMyManager;
static dispatch_once_t pred; // Lock
dispatch_once(&pred, ^{ // This code is called at most once per app
sharedMyManager = [[BPManager alloc] init];
});
}
return sharedMyManager;
}
But the problem is not solved
How about
#interface BPManager : NSObject
#property (nonatomic, copy) NSString *dbName;
#property (nonatomic, assign) int questions;
-(id) initWithDBName:(NSString*) dbName {
#end
#import "BPManager.h"
#implementation BPManager
#synthesize dbName=_dbName, questions;
+(BPManager *)singleton {
static dispatch_once_t pred;
static BPManager *shared = nil;
dispatch_once(&pred, ^{
shared = [[BPManager alloc] initWithDBName:#"database.db"];
});
return shared;
}
-(id) initWithDBName:(NSString*) dbName {
self = [super init]
if (self) self.dbName = dbName;
return self;
}
-(void)dealloc {
[_dbName release];
[super dealloc];
}
#end
BPManager *manager = [BPManager singleton];
int count = [manager questions];
The static is private to the implementation file but no reason it should be even accessible outside the singleton method. The init overrides the default implementation with the default implementation so it's useless. In Objective-C you name the getter with the var name (count), not getCount. Initializing a class twice causes an undefined behaviour. No need to synchronize or check for if==nil when you are already using dispatch_once, see Care and Feeding of Singletons. NSString should always use copy instead retain in #property. You don't need the dealloc because this is going to be active forever while your app is running, but it's just there in case you want to use this class as a non singleton . And you probably are as good with this class being an ivar in your delegate instead a singleton, but you can have it both ways.
I'm not sure whether it's the (complete) answer, but one major flaw is that you're using instance variables (self, super) in a class method, +(id)bpManager; I'm actually surprised it let you compile that at all. Change the #synchronized(self) to #synchronized(sharedMyManager), and the [[super alloc...] init] to [[BPManager alloc...] init]. And, writing that just made me realize that the problem looks like accessing a subclassed method on an object instantiated as the superclass, but that should have been overwritten in the dispatch. Shouldn't you really only need one of those anyway, why double-init like that? (And while we're there, that's a memory leak - init'd in the if() and then overwritten in the closure without releasing it.)
The solution of Jano must work well. I use this way too to create singleton object. And I don't have any problem.
For your code, I think that if you use #synchronized (it's not necessary cause your have dispatch_once_t as Jano said), you should not call return in #synchronized.
+ (BPManager *) bpManager {
#synchronized(self) {
if(sharedMyManager == nil) {
static dispatch_once_t pred; // Lock
dispatch_once(&pred, ^{ // This code is called at most once per app
sharedMyManager = [[BPManager alloc] init];
});
}
}
return sharedMyManager;
}