I have an API class, lets call it MyClass, which I want to be available thruout my whole application. Hence I put the #import into MyProject-Prefix.pch file.
Now, when I (in my appDelegate) initiate the class, I get the error message cannot init a class object..
I understand that I am initiating my class incorrectly, but I have no idea on how I should do it... and since this is not common for development (as I see it) there ain't a lot of information to find thrue google (or I am searching wrong ;) )
So, I have two questions:
Any links for called "Creating Objective-C API's for Dummys"? ;)
Having a quick look at my code, what am I doing wrong?
This is my class:
MyClass.h
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import "NSStringAdditions.h"
#import "XMLRPCResponse.h"
#import "XMLRPCRequest.h"
#import "XMLRPCConnection.h"
/**
* END required libs
*/
extern int EID;
extern NSString * SHARED_SECRET;
extern NSString * MODE;
#interface MyClass : NSObject
+ (id)initWithEid:(int)eid secret:(NSString*)secret mode:(NSString*)mode;
+ (NSArray*)web_invoice_infoWithOCR:(NSString*)ocr pno:(NSString*)pno;
+ (NSString*)the_digest:(NSString*)source;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
int EID;
NSString * SHARED_SECRET;
NSString * MODE;
NSString * URL_LIVE;
NSString * URL_BETA;
#pragma mark init / dealloc
+ (id)init {
self = [super init];
if (self)
{
}
return self;
}
+ (id)initWithEid:(int)eid secret:(NSString*)secret mode:(NSString*)mode
{
self = [super init];
if (self)
{
if (![[mode lowercaseString] isEqualToString:#"beta"] && ![[mode lowercaseString] isEqualToString:#"live"])
{
#throw ([NSException exceptionWithName:#"Invalid mode" reason:[NSString stringWithFormat:#"Invalid mode '%#' selected. Should either be 'beta' or 'live'", mode] userInfo:nil]);
}
EID = eid;
SHARED_SECRET = secret;
MODE = [mode lowercaseString];
}
return self;
}
+ (NSArray*)web_invoice_infoWithOCR:(NSString*)ocr pno:(NSString*)pno {
NSArray *params = [NSArray arrayWithObjects:ocr, EID, pno, [MyClass the_digest:[NSString stringWithFormat:#"%d:%#:%#", EID, ocr, SHARED_SECRET]], nil];
NSLog(#"Array: %#", params);
return params;
}
- (id)xmrpc_call_function:(NSString*)method parameters:(NSArray*)params
{
// Not finished yet, Warning exists
}
[...]
Having a look at my code, you'll notice the +(id)init function. I have tried -(id)init, -(id)initialize, +(id)initialize, +(void)initialize, -(void)initialize.
This is how I "load" my class in my AppDelegate:
[MyClass initWithEid:1234 secret:#"1234" mode:#"BETA"];
EDIT
I am trying to initiate my class the same way e.g. Flurry does. Example:
[FlurryAnalytics startSession:#"1234"];
You need to alloc it first:
MyClass *myClass = [[MyClass alloc] initWithEid:1234 secret:#"1234" mode:#"BETA"];
Related
I had declared constant NSArray like the below way.
static NSArray* const myArray = [NSArray arrayWithObjects:#"obj1",#"obj2",#"ibj3",#"obj4",#"obj5",nil];
But it shows an error "Initiliser elements are Not Compile time constants".
So I declared like this.But I got the error.
Please help This Case.
Thanks.
Since NSArrays are heap-allocated objects, you cannot create them in a static context. You can declare the variables, and then initialize them in a method.
So, you can do this way :-
static NSArray *myStaticArray = nil;
- (void) someMethod
{
if (myStaticArray == nil)
{
myStaticArray = [[NSArray alloc] init...];
}
}
Hope it helps you..
I don't see why you cannot use this to solve your problem. it is 100% constant, because the NSArray is immutable, and it is also 100% static.
GlobalConstant.h file:
#import <Foundation/Foundation.h>
// EDITED : __unused static NSArray *_myGlobalArray;
#interface GlobalConstant : NSObject
+ (NSArray *)myGlobalArray;
#end
GlobalConstant.m file:
#import "GlobalConstant.h"
#implementation GlobalConstant
+ (NSArray *)myGlobalArray {
static NSArray *_myGlobalArray = nil; // EDITED
#synchronized (_myGlobalArray) {
if (_myGlobalArray == nil) {
_myGlobalArray = [NSArray arrayWithObjects:#"1", #"2", nil]; // here is your array...
}
return _myGlobalArray;
}
}
#end
...and your array will be available in every class which includes the GlobalConstant.h file, you can reach the array like
NSArray *_globalArray = [GlobalConstant myGlobalArray];
To create the static array you can make use of the initialize method. This is a special class method that every class has. It is called the first time a class is referenced in any way and before any other code in the class is executed.
static NSArray* myArray = nil;
#implementation SomeClass
+ (void)initialize {
if (self == [SomeClass class]) {
myArray = #[ #"obj1", #"obj2", #"obj3", #"obj4", #"obj5" ];
}
}
// rest of methods
#end
and, to save typing, you can use NSArray* myArray = #[ #"obj1", #"obj2", #"ibj3", #"obj4", #"obj5" ];
I'm using a Singleton class and following is the code:
.h File:
#import <Foundation/Foundation.h>
#interface Credential : NSObject {
NSString *UID;
NSString *UPASS;
}
#property(nonatomic,retain) NSString *UID;
#property(nonatomic,retain) NSString *UPASS;
static Credential *credential = NULL;
+(Credential*) sharedInstance;
/*
+ #property(nonatomic,retain) NSString *UID;
+ #property(nonatomic,retain) NSString *UPASS;
*/
#end
.m file:
#import "Credential.h"
#implementation Credential
#synthesize UID,UPASS;
-(void) dealloc{
[UID release];
[UPASS release];
[super dealloc];
}
+(Credential*) sharedInstance
{
#synchronized(self)
{
if (credential == NULL) {
credential = [[Credential alloc] init];
}
}
return credential;
}
#end
The following line produces warning "defined but not used"
static Credential *credential = NULL;
I couldn't figure out that I've been using credential variable in .m file under "sharedInstance" function then why am I getting this warning?
A strange issue to me!
Does the problem go away when you move the static variable to the top of the implementation (.m) file? And on a related note, I think that you would benefit from getting rid of the singleton altogether.
I've built a singleton object to manage some data in my app
#interface MyCommon : NSObject {
NSArray *quizz;
int iCurrentQuestion;
};
+ (MyCommon *)singleton;
#property (retain) NSArray *quizz;
#property (assign) int iCurrentQuestion;
#end
MyCommon.m
#import "MyCommon.h"
// MyCommon.m:
#implementation MyCommon
static MyCommon * MyCommon_Singleton = nil;
#synthesize iCurrentQuestion;
+ (MyCommon *)singleton
{
if (nil == MyCommon_Singleton)
{
MyCommon_Singleton = [[MyCommon alloc] init];
NSLog(#"allocating MyCommon_Singleton at %#",MyCommon_Singleton);
}
else {
NSLog(#"accessing singleton : %#", MyCommon_Singleton);
}
return MyCommon_Singleton;
}
- (NSArray*) getQuizz{
return quizz;
}
- (void) setQuizz:(NSArray *)array {
quizz = [NSArray arrayWithArray:array];
NSLog(#"setQuizz : %#",quizz);
}
There is no problem for writing the quizz object (setQuizz), however when I try to access it for reading, I get a crash : the quizz looks invalid and Xcode notify me an invalid CFArrayRef
I don't know what's wrong with my code.
You provide a custom setter for quizz but it doesn't comply with how the property is declared.
You're not retaining quizz when you're setting a new value. It's likely to be released just after, leading to a crash when you access it.
You should write
- (void)setQuizz:(NSArray *)array {
if (quizz != array) {
NSArray *tmp = quizz;
quizz = [array retain]; // retain the new value
[tmp release]; // release the old one
}
NSLog(#"setQuizz : %#",quizz);
}
this is way more code than it needs to be. First if you are going to be providing your own method you should declare so in the #property declaration which you didn't. Also your not properly retaining your variables. Additionally you should be using dispatch_once() for a thread safe & fast way to guarantee the singleton is only created once.
#interface MyCommon : NSObject {}
#property(nonatomic, retain) NSArray *quiz;
#property (assign) int iCurrentQuestion;
+ (MyCommon *)singleton;
#end
#implementation MyCommon
#synthesize quiz;
#synthesize iCurrentQuestion;
-(id)init {
self = [super init];
if(self) {
quiz = [[NSMutableArray alloc init];
iCurrentQuestion = 0;
}
return self;
}
+ (MyCommon *)singleton {
static MyCommon *singleton = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
singleton = [[MyCommon alloc] init];
});
return singleton;
}
#end
then you just do
[MyCommon singleton].quiz = //some array
I made this class that turns any object into a singleton, but I know that it's not "concurrent queue safe." Could someone please explain to me how to do this, or better yet, show me the code. To be clear I want to know how to use this with operation queues and dispatch queues (NSOperationQueue and Grand Central Dispatch) on iOS.
Thanks in advance,
Rich
EDIT: I had an idea for how to do it. If someone could confirm it for me I'll do it and post the code. The idea is that proxies make queues all on their own. So if I make a mutable proxy (like Apple does in key-value coding/observing) for any object that it's supposed to return, and always return the same proxy for the same object/identifier pair (using the same kind of lazy loading technique as I used to create the singletons), the proxies would automatically queue up the any messages to the singletons, and make it totally thread safe.
IMHO this seems like a lot of work to do, so I don't want to do it if it's not gonna work, or if it's gonna slow my apps down to a crawl.
Here's my non-thread safe code:
RMSingletonCollector.h
//
// RMSingletonCollector.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "RMWeakObjectRef.h"
struct RMInitializerData {
// The method may take one argument.
// required
SEL designatedInitializer;
// data to pass to the initializer or nil.
id data;
};
typedef struct RMInitializerData RMInitializerData;
RMInitializerData RMInitializerDataMake(SEL initializer, id data);
#interface NSObject (SingletonCollector)
// Returns the selector and data to pass to it (if the selector takes an argument) for use when initializing the singleton.
// If you override this DO NOT call super.
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier;
#end
#interface RMSingletonCollector : NSObject {
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier;
+ (void)destroyCollection;
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
#end
// ==--==--==--==--==Notifications==--==--==--==--==
extern NSString *const willDestroySingletonCollection;
extern NSString *const willDestroySingletonCollectionObject;
RMSingletonCollector.m
//
// RMSingletonCollector.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMSingletonCollector.h"
#import <objc/objc-runtime.h>
NSString *const willDestroySingletonCollection = #"willDestroySingletonCollection";
NSString *const willDestroySingletonCollectionObject = #"willDestroySingletonCollectionObject";
RMInitializerData RMInitializerDataMake(SEL initializer, id data) {
RMInitializerData newData;
newData.designatedInitializer = initializer;
newData.data = data;
return newData;
}
#implementation NSObject (SingletonCollector)
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier {
return RMInitializerDataMake(#selector(init), nil);
}
#end
#interface RMSingletonCollector ()
+ (NSMutableDictionary *)singletonCollection;
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection;
#end
#implementation RMSingletonCollector
static NSMutableDictionary *singletonCollection = nil;
+ (NSMutableDictionary *)singletonCollection {
if (singletonCollection != nil) {
return singletonCollection;
}
NSMutableDictionary *collection = [[NSMutableDictionary alloc] initWithCapacity:1];
[self setSingletonCollection:collection];
[collection release];
return singletonCollection;
}
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection {
if (newSingletonCollection != singletonCollection) {
[singletonCollection release];
singletonCollection = [newSingletonCollection retain];
}
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
id obj;
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
if (obj = [[self singletonCollection] objectForKey:key]) {
return obj;
}
// dynamic creation.
// get a class for
Class classForName = NSClassFromString(className);
if (classForName) {
obj = objc_msgSend(classForName, #selector(alloc));
// if the initializer takes an argument...
RMInitializerData initializerData = [classForName designatedInitializerForIdentifier:identifier];
if (initializerData.data) {
// pass it.
obj = objc_msgSend(obj, initializerData.designatedInitializer, initializerData.data);
}
else {
obj = objc_msgSend(obj, initializerData.designatedInitializer);
}
[singletonCollection setObject:obj forKey:key];
[obj release];
}
else {
// raise an exception if there is no class for the specified name.
NSException *exception = [NSException exceptionWithName:#"com.RMDev.RMSingletonCollector.failed_to_find_class" reason:[NSString stringWithFormat:#"SingletonCollector couldn't find class for name: %#", [className description]] userInfo:nil];
[exception raise];
[exception release];
}
return obj;
}
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier {
id obj = [self collectionObjectForType:className identifier:identifier];
RMWeakObjectRef *objectRef = [[RMWeakObjectRef alloc] initWithObject:obj identifier:identifier];
return [objectRef autorelease];
}
+ (void)destroyCollection {
NSDictionary *userInfo = [singletonCollection copy];
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollection object:self userInfo:userInfo];
[userInfo release];
// release the collection and set it to nil.
[self setSingletonCollection:nil];
}
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollectionObject object:[singletonCollection objectForKey:key] userInfo:nil];
[singletonCollection removeObjectForKey:key];
}
#end
RMWeakObjectRef.h
//
// RMWeakObjectRef.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
// In order to offset the performance loss from always having to search the dictionary, I made a retainable, weak object reference class.
#import <Foundation/Foundation.h>
#protocol RMWeakObjectReference <NSObject>
#property (nonatomic, assign, readonly) id objectRef;
#property (nonatomic, retain, readonly) NSString *className;
#property (nonatomic, retain, readonly) NSString *objectIdentifier;
#end
#interface RMWeakObjectRef : NSObject <RMWeakObjectReference>
{
id objectRef;
NSString *className;
NSString *objectIdentifier;
}
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier;
- (void)objectWillBeDestroyed:(NSNotification *)notification;
#end
RMWeakObjectRef.m
//
// RMWeakObjectRef.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMWeakObjectRef.h"
#import "RMSingletonCollector.h"
#implementation RMWeakObjectRef
#dynamic objectRef;
#synthesize className, objectIdentifier;
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier {
if (self = [super init]) {
NSString *classNameForObject = NSStringFromClass([object class]);
className = classNameForObject;
objectIdentifier = identifier;
objectRef = object;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollectionObject object:object];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollection object:[RMSingletonCollector class]];
}
return self;
}
- (id)objectRef {
if (objectRef) {
return objectRef;
}
objectRef = [RMSingletonCollector collectionObjectForType:className identifier:objectIdentifier];
return objectRef;
}
- (void)objectWillBeDestroyed:(NSNotification *)notification {
objectRef = nil;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[className release];
[super dealloc];
}
#end
The easiest (and thread safe) acces to singleton:
static SomeClass* shared_instance;
+(SomeClass*) sharedInstance {
#synchronized( shared_instance ) {
if( !shared_instance ) {
shared_instance = [[SomeClass alloc] init];
//some additional initialization here
}
}
return shared_instance;
}
Edit:
I think particular singleton class would be your best choice. Even for lazy loading. E.g. you need to access some array of users all over your app. Then you can create singleton class:
UserManager:
+(UserManager*) sharedManager;
-(NSArray*) allUsers;
-(NSArray*) recentUsers;
-(NSArray*) featuredUsers;
-(void) addUser:(User*) user;
-(void) removeUser:(User*) user;
etc...
Then you will be able to access that arrays in every view controller.
You should create singleton for other types.
I would like to add some constant keys for my application, these constants can be accessed anywhere in program. So I declare the constants in interface file:
#import <UIKit/UIKit.h>
NSString * MIN_INTERVAL_KEY = #"MIN_INTERVAL_KEY";
NSString * MAX_TOBACCO_KEY = #"MAX_TOBACCO_KEY";
NSString * ICON_BADGE = #"ICON_BADGE";
#interface SmokingViewController : UIViewController {
}
And I would like to access them from the MinIntervalViewController class:
- (void)viewDidAppear:(BOOL)animated {
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
if (user) {
self.selectedValue = [user objectForKey:MIN_INTERVAL_KEY];
}
[super viewDidAppear:animated];
}
But the application shows an error in the MinIntervalViewController class:
error: 'MIN_INTERVAL_KEY' undeclared (first use in this function)
Do I miss something? Any help would be appreciated.
Thanks
Constants.h
#import <Cocoa/Cocoa.h>
#interface Constants : NSObject {
}
extern int const kExampleConstInt;
extern NSString * const kExampleConstString;
#end
Constants.m
#import "Constants.h"
#implementation Constants
int const kExampleConstInt = 1;
NSString * const kExampleConstString = #"String Value";
#end
To use:
#import "Constants.h"
Then, simply call the specific variable you wish to use.
NSString *newString = [NSString stringWithString:kExampleConstString];
In the .h file:
extern NSString * const MIN_INTERVAL_KEY;
In one (!) .m file:
NSString * const MIN_INTERVAL_KEY = #"MIN_INTERVAL_KEY";
And what you seemed to have missed is to actually import the header file declaring MIN_INTERVAL_KEY ;-) So if you declared it in SmokingViewController.h but like to use it in MinIntervalViewController.m, then you need to import "SmokingViewController.h" in your MinIntervalViewController.m. Since Objective-C is really more or less an extension to C all C visibility rules apply.
Also, what helps to debug things like that is to right-click on the .m file in Xcode and select "Preprocess". Then you see the file preprocess, i.e. after CPP has done its work. This is what the C compiler REALLY is digesting.