I'm using the Vuforia plugin within Unity 2018.1 and I have my own override for UnityAppController. When I do that the Vuforia one wipes out mine so it never gets called.
I found many possible solutions but the only one I've managed to get working is to manually replace the Vuforia one with one that also calls my own code too. Which is a total hack and not a good way forward.
I found this possible solution //Can you have more than one subclass of UnityAppController?
but I don't understand it.
#import "UnityAppController.h"
namespace {
typedef BOOL (*ApplicationDidFinishLaunchingWithOptionsImp)(UnityAppController *appController,
SEL selector,
UIApplication *application,
NSDictionary *launchOptions);
ApplicationDidFinishLaunchingWithOptionsImp OriginalApplicationDidFinishLaunchingWithOptions;
BOOL ApplicationDidFinishLaunchingWithOptions(UnityAppController *appController,
SEL selector,
UIApplication *application,
NSDictionary *launchOptions) {
// Initialize Google Play Games, etc
return OriginalApplicationDidFinishLaunchingWithOptions(appController, selector, application, launchOptions);
}
IMP SwizzleMethod(SEL selector, Class klass, IMP newImp) {
Method method = class_getInstanceMethod(klass, selector);
if (method != nil) {
return class_replaceMethod(klass, selector, newImp, method_getTypeEncoding(method));
}
return nil;
}
} // anonymous namespace
#interface AppController : UnityAppController
#end
#implementation AppController
+ (void)load {
OriginalApplicationDidFinishLaunchingWithOptions = (ApplicationDidFinishLaunchingWithOptionsImp)
SwizzleMethod(#selector(application:didFinishLaunchingWithOptions:),
[UnityAppController class],
(IMP)&ApplicationDidFinishLaunchingWithOptions);
}
#end
Where do you add your own code once you have swizzled the original?
This is the code I used to replace the Vuforia version:
#implementation VuforiaNativeRendererController
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
//printf_console("Did Finish Launching with options\n");
NSURL *URL = [launchOptions valueForKey:UIApplicationLaunchOptionsURLKey];
if (URL)
{
const char *URLString = [URL.absoluteString UTF8String];
//printf_console("Application started with URL:\n");
//printf_console("%s\n", URLString);
UnitySendMessage("Scripts", "openURLComplete", URLString);
}
BOOL ret = [super application:application didFinishLaunchingWithOptions:launchOptions];
if (ret)
{
_unityView.backgroundColor = UIColor.clearColor;
}
return ret;
}
But how would I introduce that using a "swizzle" method as above?
Related
I have an object like this:
typedef void (^ Completion) (Response *);
// Response class
#interface Response : NSObject {
NSDictionary * kdata;
}
- (id)initWithJson:(NSDictionary *)data;
#property (nonatomic, assign) NSDictionary * data;
#end
#implementation Response
- (id)initWithJson:(NSDictionary *)data { kdata = data; }
- (NSDictionary *) data { return kdata; }
- (void) setData: (NSDictionary *)data { kdata = data; }
- (NSDictionary *) msg { return kdata[#"msg"]; }
#end
// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
NSDictionary * json = // get from networking function, which will always have key "msg".
Response * responseObj = [[Response alloc] initWithJson:json];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion != nil) { completion (responseObj); }
});
}
// inside caller method
[X doSomething:^(Response * response) {
NSLog (#"%#", [response msg]);
}
This code will raise error on accessing kdata[#"msg"], even though I'm sure from the debug that the object was initialised properly with a dictionary contains key "msg". When I debug the object, on the watch window, it shows me that the kdata data type keeps changing, from NSArrayM, NSSet, NSDictionary, etc. And its contents also keep changing. I even add retain keyword when calling completion ([responseObj retain]); but still produce error.
But if the code in class X is changed into like this:
// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
NSDictionary * json = // get from networking function, which will always have key "msg".
Response * responseObj = [[Response alloc] initWithJson:json];
if (completion != nil) { completion (responseObj); } // here is the change, no more switching to main thread
}
// inside caller method - no change here
[X doSomething:^(Response * response) {
NSLog (#"%#", [response msg]);
}
The code works perfectly. Why is that happened? This is built in Xcode without ARC.
EDIT: someone mentioned about the init. This is my mistake that what was written above is not exactly my code, and I copy the init method wrong. This is my init method:
- (instancetype) initWithData:(NSDictionary *)freshData {
NSParameterAssert(freshData); // make sure not nil
self = [super init];
if (self) {
kdata = freshData;
}
return self;
}
The problem is the object get's released right when you call the 'async' .
The way you declared your object is added to the autorelease pool since the control does not wait for 'async' to complete and the control return's by reaching the end of function 'doSomething' and releasing it's local objects which were added to the autorelease pool, and after that the memory location is used for other data and that's what you see confusing data.
I think by adding the __block specifier in front of your declaration you instruct the code to capture this object in following blocks strongly and release it when the block finished executing. Give it a try.
// inside a networking class X implementation
- (void) doSomething:(completionBlock)completion {
NSDictionary * json = // get from networking function, which will always have key "msg".
__block Response * responseObj = [[Response alloc] initWithJson:json];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion != nil) { completion (responseObj); }
});
}
- (id)initWithJson:(NSDictionary *)data { kdata = data; }
You need call supers init here and return self.
Start to learn basics.
I cannot for the life of me get an event to properly send from iOS native across the bridge to the react native JS context. On the Objective-C side I want to have a module to easily send events across the bridge. I have called this class EventEmitter and its definition is as follows:
// EventEmitter.h
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#interface EventEmitter : NSObject<RCTBridgeModule>
- (void)emitEvent:(NSString *) eventName withData:(id) eventData;
#end
and the implementation:
// EventEmitter.m
#import "EventEmitter.h"
#implementation EventEmitter
RCT_EXPORT_MODULE();
#synthesize bridge = _bridge;
- (void)emitEvent:(NSString *) eventName withData:(id) eventData
{
NSLog( #"emitting %# with data %#", eventName, [eventData description] );
[[_bridge eventDispatcher] sendDeviceEventWithName:eventName body:eventData];
[[_bridge eventDispatcher] sendAppEventWithName:eventName body:eventData];
}
#end
I'm using both sendDeviceEvent and sendAppEvent because I can't get either to work.
On the JS side of things I register to receive these events in the global namespace of one of my modules (so that I know the event subscription will happen before the event is emitted). I register like this:
console.log( 'ADDING EVENT LISTENERS' );
NativeAppEventEmitter.addListener( 'blah', test => console.log( 'TEST1', test ) );
DeviceEventEmitter.addListener( 'blah', test => console.log( 'TEST2', test ) );
In my log statements I get the following:
2016-03-19 12:26:42.501 [trace][tid:com.facebook.React.JavaScript] ADDING EVENT LISTENERS
2016-03-19 12:26:43.613 [name redacted][348:38737] emitting blah with data [data redacted]
So I can tell that I am sending both an app event and a device event with the tag blah and I have registered to listen for the blah event with both the DeviceEventEmitter and NativeAppEventEmitters but I'm not getting called back in the listeners.
What am I doing wrong?? Thanks for reading!
You can using NativeEventEmitter
// register eventEmitter
const {NGListener} = NativeModules; // NSListener is my class
this.eventEmitter = new NativeEventEmitter(NativeModules.NGListener);
this.eventEmitter.addListener('CancelEvent', (data) => {
console.log(data);
})
In ObjectiveC , you can create
#import <RCTViewManager.h>
#import <RCTEventEmitter.h>
#interface NGListener: RCTEventEmitter <RCTBridgeModule>
#end
#implementation NGListener
RCT_EXPORT_MODULE();
- (NSArray<NSString*> *)supportedEvents {
return #[#"CancelEvent", #"OKEvent"];
}
// And you sent event you want from objectC to react-native
[self sendEventWithName:#"CancelEvent" body:#"Tap`enter code here` on Cancel button from Objc"];
I wrote sample example to handle event from react-native to objectivec and opposite.
https://github.com/lengocgiang/event-listener
Hope this help!!
I've tried dispatching events and it seems bridge is not initialised when you create new EventEmitter instances manually by using [EventEmitter alloc] init]
You should let react-native create instances. I checked native components and they're using -(void)setBridge:(RCTBridge *)bridge method to do initialisation work. Please check out RCTLinkingManager to see an example. It's using NSNotificationCenter to handle events.
// registering for RCTOpenURLNotification evet when the module is initialised with a bridge
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleOpenURLNotification:)
name:RCTOpenURLNotification
object:nil];
}
// emitting openURL event to javascript
- (void)handleOpenURLNotification:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:#"openURL"
body:notification.userInfo];
}
// creating RCTOpenURLNotification event to invoke handleOpenURLNotification method
+ (BOOL)application:(UIApplication *)application
openURL:(NSURL *)URL
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
NSDictionary<NSString *, id> *payload = #{#"url": URL.absoluteString};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification
object:self
userInfo:payload];
return YES;
}
In my case I got this working by keeping a value of the bridge from RCTRootView and passing it to the Emitter Instance.
#implementation AppDelegate {
RCTBridge *rootBridge;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
......
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:#"MyApp"
initialProperties:nil
launchOptions:launchOptions];
rootBridge = rootView.bridge;
.......
}
- (IBAction)doAction:(id)sender {
BridgeEvents *events = [[BridgeEvents alloc] init];
[events setBridge:rootBridge];
[events doMyAction];
}
In my Emitter Class:
#import "RCTEventEmitter.h"
#interface BridgeEvents : RCTEventEmitter <RCTBridgeModule>
- (void)doMyAction;
#end
#import "BridgeEvents.h"
#implementation BridgeEvents
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents {
return #[#"onEvent"];
}
- (void)doMyAction {
[self sendEventWithName:#"onEvent" body:#""];
}
#end
RNNotification *notification = [RNNotification allocWithZone: nil];
[notification sendNotificationToReactNative]I tried everything above and was not able to get it work in my app.
Finally this worked for me.
#import "RNNotification.h"
#implementation RNNotification
RCT_EXPORT_MODULE();
+ (id)allocWithZone:(NSZone *)zone {
static RNNotification *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
- (NSArray<NSString *> *)supportedEvents
{
return #[#"EventReminder"];
}
- (void)sendNotificationToReactNative
{
[self sendEventWithName:#"EventReminder" body:#{#"name": #"name"}];
}
and while initing the function
RNNotification *notification = [RNNotification allocWithZone: nil];
[notification sendNotificationToReactNative]
You have to use your emitter class like this
[[self.bridge moduleForClass:[RNNotification class]] sendNotificationToReactNative];
You can try following solution to to send event from iOS to React Native
RNEventEmitter.m
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface RCT_EXTERN_MODULE(RNEventEmitter, RCTEventEmitter)
RCT_EXTERN_METHOD(supportedEvents)
#end
RNEventEmitter.swift
import Foundation
#objc(RNEventEmitter)
open class RNEventEmitter: RCTEventEmitter {
public static var emitter: RCTEventEmitter!
override init() {
super.init()
RNEventEmitter.emitter = self
}
open override func supportedEvents() -> [String] {
["onReady", "onPending", "onFailure"] // etc.
}
}
Your file from where you are going to emit event add below line
RNEventEmitter.emitter.sendEvent(withName: "onReady", body: [["status":"Connected","data":["name":dev?.name,"deviceId":dev?.identifier]]]);
React Native file
const emitter = new NativeEventEmitter(NativeModules.RNEventEmitter)
In your Bridgin-Header file import
#import <React/RCTEventEmitter.h>
In your useEffect
emitter.addListener('onReady', (data: any) => {
console.log("addListener", data);
Alert.alert(JSON.stringify(data))
});
I'd like my class to detect that a new instance is equivalent (vis a vis isEqual: and hash) to some existing instance, and create only unique instances. Here's code that I think does the job, but I'm concerned it's doing something dumb that I can't spot...
Say it's an NSURLRequest subclass like this:
// MyClass.h
#interface MyClass : NSMutableURLRequest
#end
// MyClass.m
#implementation MyClass
+ (NSMutableSet *)instances {
static NSMutableSet *_instances;
static dispatch_once_t once;
dispatch_once(&once, ^{ _instances = [[NSMutableSet alloc] init];});
return _instances;
}
- (id)initWithURL:(NSURL *)URL {
self = [super initWithURL:URL];
if (self) {
if ([self.class.instances containsObject:self])
self = [self.class.instances member:self];
else
[self.class.instances addObject:self];
}
return self;
}
// Caller.m
NSURL *urlA = [NSURL urlWithString:#"http://www.yahoo.com"];
MyClass *instance0 = [[MyClass alloc] initWithURL: urlA];
MyClass *instance1 = [[MyClass alloc] initWithURL: urlA]; // 2
BOOL works = instance0 == instance1; // works => YES, but at what hidden cost?
Questions:
That second assignment to self in init looks weird, but not insane.
Or is it?
Is it just wishful coding to think that second alloc (of instance1) gets magically cleaned up?
It's not insane, but in manual retain/release mode, you do need to release self beforehand or you'll leak an uninitialized object every time this method is run. In ARC, the original instance will automatically be released for you.
See #1.
BTW, for any readers who usually stop at one answer, bbum's answer below includes a full working example of a thread-safe implementation. Highly recommended for anyone making a class that does this.
Thought of a better way (original answer below the line) assuming you really want to unique by URL. If not, this also demonstrates the synchronization primitive use.
#interface UniqueByURLInstances:NSObject
#property(strong) NSURL *url;
#end
#implementation UniqueByURLInstances
static NSMutableDictionary *InstanceCache()
{
static NSMutableDictionary *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [NSMutableDictionary new];
});
return cache;
}
static dispatch_queue_t InstanceSerializationQueue()
{
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("UniqueByURLInstances queue", DISPATCH_QUEUE_SERIAL);
});
return queue;
}
+ (instancetype)instanceWithURL:(NSURL*)URL
{
__block UniqueByURLInstances *returnValue = nil;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (!returnValue)
{
returnValue = [[self alloc] initWithURL:URL];
}
});
return returnValue;
}
- (id)initWithURL:(NSURL *)URL
{
__block UniqueByURLInstances* returnValue = self;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (returnValue) return;
returnValue = [super initWithURL:URL];
if (returnValue) {
[InstanceCache() setObject:returnValue forKey:URL];
}
_url = URL;
});
return returnValue;
}
- (void)dealloc {
dispatch_sync(InstanceSerializationQueue(), ^{
[InstanceCache() removeObjectForKey:_url];
});
// rest o' dealloc dance here
}
#end
Caveat: Above was typed into SO -- never been run. I may have screwed something up. It assumes ARC is enabled. Yes, it'll end up looking up URL twice when using the factory method, but that extra lookup should be lost in the noise of allocation and initialization. Doing that means that the developer could use either the factory or the initializer and still see unique'd instances but there will be no allocation on execution of the factory method when the instance for that URL already exists.
(If you can't unique by URL, then go back to your NSMutableSet and skip the factory method entirely.)
What Chuck said, but some additional notes:
Restructure your code like this:
+(NSMutableSet*)instances
{
static NSMutableSet *_instances;
dispatch_once( ...., ^{ _instances = [[NSMutableSet alloc] init];});
return instances;
}
Then call that method whenever you want access to instances. It localizes all the code in one spot and isolates it from +initialize (which isn't really a big deal).
If your class may be instantiated from multiple threads, you'll want to surround the check-allocate-or-return with a synchronization primitive. I would suggest a dispatch_queue.
Pre-automatic reference counting, you could do the appropriate pointer casts in Objective-c to allow you to use bool OSAtomicCompareAndSwapPtr(void* oldValue, void* newValue, void* volatile *theValue); to attempt to atomically swap pointers when dealing with multithreaded accesses.
Under ARC these pointer casts are not valid. Is there an equivalent atomic pointer swap available under ARC for iOS? I was hoping to avoid the more expensive locking if this alternative is still available.
Disclaimer: code in this answer is not tested!
First of all I'd like to mention that most pointer uses don't really need compare-and-swap. C pointer reads and writes are atomic by themselves. See this SO answer for more detail. Same goes for ARM. So if you implement atomic getters and setters you only need a memory barrier to guarantee that other threads see fully initialized objects:
NSObject * global;
NSObject * get() { return global; }
void set(NSObject * value) { OSMemoryBarrier(); global = value; }
Now back to the question, because who knows, maybe there are real uses for compare-and-swapping objects. The casts are still possible, you just declare them differently now:
NSString * a = #"A";
NSObject * b = #"B";
OSAtomicCompareAndSwapPtrBarrier(
(__bridge void *)a,
(__bridge void *)b,
(__bridge void * *)&a);
However this code has a problem: the string #"A" loses a reference, and #"B" gets referenced twice, without ARC knowing. Therefore #"A" will leak, and the program will likely crash when leaving the scope because #"B" will be released twice while only having the retain counter of 1.
I think the only option is to use Core Foundation objects. You can use the fact that NSObject is toll-free bridged with CFType. I couldn't find any definitive documentation on this but it follows from common sense and practical evidence. So e.g. it is possible to implement a singleton:
CFTypeRef instance;
Thingamabob * getInstance() {
if (!instance) {
CFTypeRef t = (__bridge_retained CFTypeRef)[Thingamabob new];
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, t, &instance)) {
CFRelease(t);
}
}
return (__bridge Thingamabob *)instance;
}
You may be able to use this easily, if one condition is met, and maybe if you're willing to play games.
Create a new file, mark it as not using ARC in the Build Phase, and put this swap into a small C function. At the top of the function get the object's retainCounts, and if they are equal (and you have reason to believe they are not sitting in an autorelease pool) you can just swap them, as ARC will insure the proper releases to each.
If they are not equal, well, you can play games by changing the retain count.
I found these comparable classes - before and update which help illustrate update
BEFORE
#import "SBlockDisposable.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
#interface SBlockDisposable ()
{
void *_block;
}
#end
#implementation SBlockDisposable
- (instancetype)initWithBlock:(void (^)())block
{
self = [super init];
if (self != nil)
{
_block = (__bridge_retained void *)[block copy];
}
return self;
}
- (void)dealloc
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
strongBlock = nil;
}
}
}
}
- (void)dispose
{
void *block = _block;
if (block != NULL)
{
if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
{
if (block != nil)
{
__strong id strongBlock = (__bridge_transfer id)block;
((dispatch_block_t)strongBlock)();
strongBlock = nil;
}
}
}
}
#end
AFTER
//
// KAGRACDisposable.m
// ReactiveCocoa
//
// Created by Josh Abernathy on 3/16/12.
// Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
#import "KAGRACDisposable.h"
#import "KAGRACScopedDisposable.h"
#import <stdatomic.h>
#interface KAGRACDisposable () {
// A copied block of type void (^)(void) containing the logic for disposal,
// a pointer to `self` if no logic should be performed upon disposal, or
// NULL if the receiver is already disposed.
//
// This should only be used atomically.
void * volatile _disposeBlock;
}
#end
#implementation KAGRACDisposable
#pragma mark Properties
- (BOOL)isDisposed {
return _disposeBlock == NULL;
}
#pragma mark Lifecycle
- (id)init {
self = [super init];
if (self == nil) return nil;
_disposeBlock = (__bridge void *)self;
atomic_thread_fence(memory_order_seq_cst);
return self;
}
- (id)initWithBlock:(void (^)(void))block {
NSCParameterAssert(block != nil);
self = [super init];
if (self == nil) return nil;
_disposeBlock = (void *)CFBridgingRetain([block copy]);
atomic_thread_fence(memory_order_seq_cst);
return self;
}
+ (instancetype)disposableWithBlock:(void (^)(void))block {
return [[self alloc] initWithBlock:block];
}
- (void)dealloc {
if (_disposeBlock == NULL || _disposeBlock == (__bridge void *)self) return;
CFRelease(_disposeBlock);
_disposeBlock = NULL;
}
#pragma mark Disposal
- (void)dispose {
void (^disposeBlock)(void) = NULL;
while (YES) {
void *blockPtr = _disposeBlock;
if (atomic_compare_exchange_strong((volatile _Atomic(void*)*)&_disposeBlock, &blockPtr, NULL)) {
if (blockPtr != (__bridge void *)self) {
disposeBlock = CFBridgingRelease(blockPtr);
}
break;
}
}
if (disposeBlock != nil) disposeBlock();
}
#pragma mark Scoped Disposables
- (KAGRACScopedDisposable *)asScopedDisposable {
return [KAGRACScopedDisposable scopedDisposableWithDisposable:self];
}
#end
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;
}