I'm new to IOS development. I wrote the implementation file following like this.
#implementation Utils
+(id)alloc
{
return [self instance];
}
+(Utils *)instance
{
static Utils *utils = nil;
if (!utils) {
utils = [self init];
}
return utils;
}
-(Utils *)init
{
self = [super init];
if (self) {
mConst = [Constants instance];
mCONT_REGEXP = [mConst CONT_REGEXP];
}
return self;
}
When i call
[Utils instance];
I got the error following Like this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[Utils<0xbff54> init]: cannot init a class object.'
Thanks for your answers.
Are you trying to create a shared singleton instance?
In that case, use the following snippet:
+ (id)sharedInstance;
{
static dispatch_once_t onceToken;
static Utils *sharedUtilsInstance = nil;
dispatch_once( &onceToken, ^{
sharedUtilsInstance = [[Utils alloc] init];
});
return sharedUtilsInstance;
}
It is better to call it "sharedInstance" so that it's more understandable the instance is shared.
remove your following method
+(id)alloc
{
return [self instance];
}
And write code as/....
+(Utils *)instance
{
static Utils *utils = nil;
if (!utils) {
unit = [[unit alloc] init];
}
return utils;
}
Related
In your opinion if I have a singleton subclass of NSObject being initialised with parameters like this:
- (MyObject *) initWithSomeParam:(NSString *)param{
self = [super init];
if (SharedInstance == nil){
SharedInstance = [super init];
SharedInstance.someProperty = param;
}
return self;
}
+ (MyObject *) objectWithSomeParam:(NSString *)param{
return [[self alloc] initWithSomeParam:param];
// Will the alloc cause a leak?
}
The user doesn't have access to the instance method, just the class. Thanks.
That's not the normal way of implementing a singleton and you are breaking the convention of init. Better would be to create a sharedInstance class method and leave the initWithParam method to be more conventional:
static MyObject *_sharedInstance = nil;
+ (MyObject *)sharedInstance:(NSString *)param
{
if (_sharedInstance == nil)
{
_sharedInstance = [MyObject alloc] initWithParam:param];
}
return _sharedInstance;
}
// This must be called during app termination to avoid memory leak
+ (void)cleanup
{
[_sharedInstance release];
_sharedInstance = nil;
}
- (id)initWithParam:(NSString *)param
{
self = [super init];
if (self != nil)
{
self.someProperty = param;
}
return self;
}
However, even that doesn't seem very comfortable; i.e. what happens if the user calls sharedInstance with a different parameter? Perhaps you want to keep a NSMutableDictionary of the initialized objects and create/return them depending on the parameter?
If so, you would do:
static NSMutableDictionary _sharedInstances = [[NSMutableDictionary alloc] init];
+ (MyObject *)sharedInstance:(NSString *)param
{
MyObject *obj = [_sharedInstances objectForKey:param];
if (obj == nil)
{
obj = [[MyObject alloc] initWithParam:param];
[_sharedInstances setObject:obj forKey:param];
}
return obj;
}
// This must be called during app termination to avoid memory leak
+ (void)cleanup
{
[_sharedInstances release];
_sharedInstances = nil;
}
I know 2 ways. What is better? And anything better than 2 ways?
+ (MyClass *)shared {
/*
static MyClass *sharedInstance = nil;
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
*/
/*
static dispatch_once_t pred;
static MyClass *sharedInstance = nil;
dispatch_once(&pred, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
*/
}
Can also create a your class instance in AppDelegate and use it anywhere in your project.
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appappDelegate.<yourClassInstance>
Here is another way to setup you shared instance. Thread safety is handled by the runtime and the code is very straight forward. This is usually how I setup my singletons. If the singleton object uses lots of resources but may not used then the dispatch_once approach works well.
static MyClass *sharedInstance = nil;
+ (void) initialize
{
sharedInstance = [[MyClass alloc] init];
}
+ (MyClass*)sharedInstance
{
return sharedInstance;
}
Just use the dispatch_once version - it's reliable and clean. Besides, it will work also with ARC - unlike the approach suggested above.
Here's some Details
+ (YourClass *)sharedInstance
{
// structure used to test whether the block has completed or not
static dispatch_once_t p = 0;
// initialize sharedObject as nil (first call only)
__strong static id _sharedObject = nil;
// executes a block object once and only once for the lifetime of an application
dispatch_once(&p, ^{
_sharedObject = [[self alloc] init];
});
// returns the same object each time
return _sharedObject;
}
The second one looks better, but this is still not perfect. Check Apple's recommendation:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-97333-CJBDDIBI
I'm having a problem with MKAnnotation, i created a class "cgdMapAnnotation" for annotations and one of it's constructor is like this:
+ (id) initWithCoordinate:(CLLocationCoordinate2D)coordinate andTitle:(NSString*) title andSubtitle:(NSString*) subtitle {
self = [super alloc];
_coordinate = coordinate;
_title = [title retain];
_subtitle = [subtitle retain];
return self;
}
The problem is that when i call:
cgdMapAnnotation *placemark=[[[cgdMapAnnotation alloc] initWithCoordinate:centerCoordinate andTitle:#"Title" andSubtitle:#"SubTitle" ] autorelease];
I get in the console the following error:
-[cgdMapAnnotation initWithCoordinate:andTitle:andSubtitle:]: unrecognized selector sent to instance 0x33cf2fe0
I really don't understand what's the problem. Can someone help?
Thanks in advance.
First, convention has it that class names start with a capital letter. So cgdMapAnnotation should be CgdMapAnnotation or CGDMapAnnotation.
Second, there are a few problems with initWithCoordinate:andTitle:andSubtitle:.
It is declared as a class method using the '+' at the beginning of the name, but you are attempting to use it as an instance method. [cgdMapAnnotation alloc] will return an instance of cgdMapAnnotation. So you are
self = [super alloc] does not make sense in this class method.
Your method should probably look like this:
- (id)initWithCoordinate:(CLLocationCoodinate2D)coordinate andTitle:(NSString*) title andSubtitle:(NSString*) subtitle
{
if( self = [super init] )
{
_coordinate = coordinate;
_title = [title retain];
_subtitle = [subtitle retain];
}
return self;
}
i have been using mostly the appDelegate class for global variables but recently i came to know that its not a gud way for keeping global variables so i am trying to make a singleton class as following
#implementation globalVar
static globalVar *_sharedInstance =nil;
#synthesize totalTime;
- (id) init
{
if (self = [super init])
{
}
return self;
}
+ (globalVar *) sharedInstance
{
#synchronized (self) {
if (_sharedInstance == nil) {
[[self alloc] init];
}
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (_sharedInstance == nil) {
_sharedInstance = [super allocWithZone:zone];
return _sharedInstance;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
-(NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
// do nothing
}
- (id)autorelease
{
return self;
}
- (void) setTotalTime:(NSString *)time
{
#synchronized(self) {
if (totalTime != time) {
[totalTime release];
totalTime = [NSString stringWithFormat:#"%#",time];
}
}
//NSLog(#"time %#",totalTime);
}
-(NSString *)getTotalTime
{
#synchronized(self) {
//NSLog(#"total %#",totalTime);
return totalTime;
}
}
when i set the value for totaltime in my appDelegate class and retrieve it in that class only i get the correct value. but when i only retrieve the value in some other class i get BAD EXCESS. i first create the sharedinstance and then only call this method then why am i getting this error??
globalVar *myEngine = [globalVar sharedInstance];
NSLog(#"about %#",[myEngine totalTime]);
in my app delegate
globalVar *myEngine = [globalVar sharedInstance];
[myEngine setTotalTime:totalTime];
NSLog(#"in app delegate%#",[myEngine getTotalTime]);
You're releasing totalTime but not retaining the new value, which means that when you access it it's already been released, causing a bad access exception.
You can correct this by changing the line where you set the value to include a call to retain:
totalTime = [[NSString stringWithFormat:#"%#",time] retain];
Have a look at the discussion here:
Is it good practice to use AppDelegate for data manipulation and Handling?
I'm a totally noob in IPhone development. Just start a week ago. I'm trying to have this tableview working.
I have a class I created called CustomerRepository with a method like this
- (CustomerRepository *) init {
self = [super init];
if (self) {
customerArray = [[NSMutableArray alloc] init];
Customer *cust1 = [[Customer alloc] init];
cust1.name = #"cust1";
[customerArray addObject: cust1];
[cust1 release];
}
return self;
}
- (NSMutableArray *) GetAll {
NSMutableArray *returnCustomerArray = [[[NSMutableArray alloc] init] autorelease];
for(Customer *cust in customerArray)
{
Customer *copy = [[Customer alloc]init];
copy.name = cust.name;
[returnCustomerArray addObject:copy];
[copy release];
}
return returnCustomerArray;
}
Now In my Controller
#synthezise customerArray;
viewDidLoad {
CustomerRepository *custRepo = [[CustomerRepository alloc] init];
customerArray = [custRepo GetAll];
[custRepo release];
}
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section {
// It always throw an exception here about -[UITableViewRowData count]: unrecognized selector sent to instance
return [customerArray count];
}
There is definitely something wrong with my code, can you guys help me point out what is wrong. Most probably an access to an instance that is already release ....
You need to retain the array returned by GetAll. Try this:
viewDidLoad {
CustomerRepository *custRepo = [[CustomerRepository alloc] init];
customerArray = [[custRepo GetAll] retain];
[custRepo release];
}
If you don't retain it, then your autorelease in -GetAll means that the returned array will eventually be released. When your -numberOfRowsInSection method fires, it's talking to a dealloc'd instance.