I am using the following NSManagedObject which was automatically generated by Xcode:
#interface Portion : NSManagedObject
{
}
#property (nonatomic, retain) NSNumber * volume;
I would like to create a custom getter/setter to convert between ml/oz depending on what the user has set, that way the database always stores the same value and it is automatically converted to the preferred units. My latest attempt looks like this:
#import "Portion.h"
#import "SettingHandler.h"
#define MILLILITERS_PER_OUNCE 29.5735296
#implementation Portion
#dynamic volume;
- (void) setVolume:(NSNumber *) number {
if ([SettingHandler getUnitsTypeShort] == #"oz") {
[self setValue:number forKey:#"volume"];
} else {
[self setValue:[NSNumber numberWithFloat:[number floatValue] * MILLILITERS_PER_OUNCE] forKey:#"volume"];
}
}
- (NSNumber *) volume {
if ([SettingHandler getUnitsTypeShort] == #"oz") {
return [self valueForKey:#"volume"];
} else {
return [NSNumber numberWithDouble: [[self valueForKey:#"volume"] floatValue] * MILLILITERS_PER_OUNCE];
}
}
The setVolume call ends up invoking itself causing an infinite loop. I'm guessing there is a way to do this but I don't know what it is, any ideas?
Sorry to play devil's advocate, but IMO, it seems like you're trying to address how a value is displayed to the user at too low of a level (in the model object itself; see The Model-View-Controller Design Pattern). Why not use a formatter that works more at the view level to help format the raw NSNumber value to a string that will be presented to the user?
You'd then have a reusable class you could use anywhere you use a number value that represents a volume. The formatter would store a "unitsType" value so it would know how to format the incoming number properly.
I did a quick version by using one of my existing formatters, MDFileSizeFormatter, as a starting point:
#import <Foundation/Foundation.h>
enum {
MDVolumeFormatterMetricUnitsType = 1,
MDVolumeFormatterOurStupidAmericanUnitsType = 2,
MDVolumeFormatterDefaultUnitsType = MDVolumeFormatterMetricUnitsType
};
typedef NSUInteger MDVolumeFormatterUnitsType;
#interface MDVolumeFormatter : NSFormatter {
MDVolumeFormatterUnitsType unitsType;
NSNumberFormatter *numberFormatter;
}
- (id)initWithUnitsType:(MDVolumeFormatterUnitsType)aUnitsType;
#property (assign) MDVolumeFormatterUnitsType unitsType;
#end
Then the .m file:
#import "MDVolumeFormatter.h"
#define MILLILITERS_PER_OUNCE 29.5735296
#implementation MDVolumeFormatter
#synthesize unitsType;
- (id)init {
return [self initWithUnitsType:MDVolumeFormatterDefaultUnitsType];
}
- (id)initWithUnitsType:(MDVolumeFormatterUnitsType)aUnitsType {
if (self = [super init]) {
numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormat:#"#,###.#"];
[self setUnitsType:aUnitsType];
}
return self;
}
- (void)dealloc {
[numberFormatter release];
[super dealloc];
}
- (NSString *)stringForObjectValue:(id)anObject {
if ([anObject isKindOfClass:[NSNumber class]]) {
NSString *string = nil;
if (unitsType == MDVolumeFormatterMetricUnitsType) {
string = [[numberFormatter stringForObjectValue:
[NSNumber numberWithFloat:
[(NSNumber *)anObject floatValue] * MILLILITERS_PER_OUNCE]]
stringByAppendingString:#" mL"];
} else {
string = [[numberFormatter stringForObjectValue:anObject] stringByAppendingString:#" oz"];
}
return string;
}
return nil;
}
#end
This could potentially be expanded to do tests on the incoming value and automatically determine the appropriate volume unit. For example, if the floatValue was 16.0, you could use if then logic to return a string of "2.0 cups" instead of 16 oz.
Check out "Custom Attribute and To-One Relationship Accessor Methods" in the Core Data Programming Guide. Basically, you should use the primitive getter/setter methods to access/change the value and wrap those calls with KVO notifications.
You will need to add declarations for the primitive accessors:
#interface Portion (PrimitiveAccessors)
- (NSNumber *)primitiveVolume;
- (void)setPrimitiveVolume:(NSNumber *)number;
#end
Then you need to replace each occurrence of:
[self setValue:number forKey:#"volume"];
with:
[self willChangeValueForKey:#"volume"];
[self setPrimitiveVolume:number];
[self didChangeValueForKey:#"volume"];
And make the corresponding changes in the getter.
Try using [self setPrimitiveValue:number forKey:#"volume"]; instead.
I would suggest yet another approach. Decide on canonical representation - either ounces or milliliters. This be what volume is actually stored as. Then declare the following getters/setters:
- (void) setOunces:(double)ounces;
- (double) ounces;
- (void) setMilliliters:(double)milliliters;
- (double*) milliliters;
If the canonical volume is milliliters, then:
- (void) setOunces:(double)ounces
{
[self setVolume:[NSNumber numberWithDouble:(ounces* MILLILITERS_PER_OUNCE)]];
}
- (double) ounces
{
return [[self volume] doubleValue]/MILLILITERS_PER_OUNCE;
}
- (void) setMilliliters:(double)milliliters
{
[self setVolume:[NSNumber numberWithDouble:milliliters]];
}
- (double) milliliters
{
return [[self volume] doubleValue];
}
Related
i am working on a project in wich i want to use Singleton Pattern model.
i want to any data model of my this project fallow Singleton Pattern.
i study the apple documentation regarding this
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaDesignPatterns/CocoaDesignPatterns.html#//apple_ref/doc/uid/TP40002974-CH6-SW6
and
http://www.oodesign.com/singleton-pattern.html
http://www.daveoncode.com/2011/12/19/fundamental-ios-design-patterns-sharedinstance-singleton-objective-c/
now i know my custom object classes should fallow the main rule of allocing a object but the i need the complete implementation like using of this class object
i am new in iphone app development so if i am wrong in any place in this Question please guide
Try this:
#implementation Singleton
+ (Singleton *)sharedInstance
{
static Singleton *obj = nil;
if (obj == nil)
obj = [[self alloc] init];
return obj;
}
#end
static MyClass *_sharedInstance;
+ (MyClass *)sharedMyClass
{
#synchronized([MyClass class]) {
if (_sharedInstance == nil)
[[self alloc] init];
return _sharedInstance;
}
return nil;
}
+(id) alloc
{
#synchronized([MyClass class]) {
NSAssert(_sharedInstance == nil, #"Attempted to allocate a second instance of MyClass.");
_sharedInstance = [super alloc];
return _sharedInstance;
}
return nil;
}
+ (id) allocWithZone:(NSZone *)zone
{
#synchronized([MyClass class]) {
NSAssert(_sharedInstance == nil, #"Attempted to allocate a second instance of MyClass.");
_sharedInstance= [super allocWithZone:zone];
return _sharedInstance;
}
return nil; //on subsequent allocation attempts return nil
}
- (id) copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax;
}
- (oneway void)release
{
// Do nothing
}
- (id)autorelease
{
return self;
}
If you can target iOS 4 or above, I will take the following way:
//.h
+(MySingletonClass *)mySharedInstance;
-(void)doSomething;
//.m
+(MySingletonClass *)mySharedInstance {
static dispatch_once_t pred;
static MySingletonClass *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingletonClass alloc] init];
});
return shared;
}
-(void)doSomething
{
}
// override also the init if you want
To access it, do an #import MySingletonClass.h and use it wherever you want like the following:
MySingletonClass* mySharedInstance = [MySingletonClass mySharedInstance];
[mySharedInstance doSomething];
I want to any data model of my this project fallow Singleton Pattern.
Based on my experience, I would not abuse on singletons. The application could become difficult to maintain. To avoid this, put the data models within your singleton. You can access data model directly (creating properties around them) or using public methods (like for example doSomething) as wrappers.
Hope this helps.
This might be a useful reference: http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html
Typically I create the object in the
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
method and have it live in that object. I make it available to the rest of the app with a macro:
#define APPLICATION ((AppDelegate*)([UIApplication sharedApplication].delegate))
as a readonly property of the app delegate
The benefit of this type of approach if you are in to mocking is that it is just another object property rather than a hidden static object.
I use:
#import <Foundation/Foundation.h>
#interface iCode_Framework : NSObject
#property (readonly, nonatomic) unsigned int iBufCapacity;
#property (readonly, nonatomic) unsigned int iPort;
#property (readonly, nonatomic) NSString * urlStr;
#end
#import "iCode_Framework.h"
static iCode_Framework * instance;
#implementation iCode_Framework
#dynamic iBufCapacity;
#dynamic iPort;
#dynamic urlStr;
- (unsigned int)iBufCapacity
{
return 1024u;
};
- (unsigned int)iPort
{
return 1978u;
};
- (NSString *)urlStr
{
return #"localhost";
};
+ (void)initialize
{
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
+ (id)allocWithZone:(NSZone * const)notUsed
{
return instance;
}
#end
Which is used exactly like a normal class, you call alloc and init! It is often convenient to assign to a variable to give a shorthand, because alloc and init are long to type, e.g.:
#import "iCode_FrameworkTests.h"
#import "iCode_Framework.h"
static iCode_Framework * c;
#implementation iCode_FrameworkTests
+ (void)initialize
{
c = [[iCode_Framework alloc] init];
}
- (void)setUp
{
[super setUp];
// Set-up code here.
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testSingletonNotNil
{
STAssertNotNil(c, nil);
}
- (void)testSingletonProperty
{
STAssertEqualObjects(c, [iCode_Framework alloc], nil);
}
- (void)testIBufCapacity
{
STAssertEquals(c.iBufCapacity, 1024u, nil);
}
#end
The advantage of this approach is it is used exactly like any other class and can therefore be mocked for testing.
Removing annotations from my map view in the following way:
if ([[self.mapView annotations] count] > 0)
{
[self.mapView removeAnnotations:[self.mapView annotations]];
}
causes my application to crash with the following exception:
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <MKAnnotationContainerView 0xe87b420> for the key path "title" from <PFAnnotation 0x10851230> because it is not registered as an observer.'
The annotations are added in the following way:
CLLocationCoordinate2D pinPosition;
for (int index = 0; index < [array count]; index++)
{
Station *aStation = [array objectAtIndex:index];
PFAnnotation *stationPin = [[PFAnnotation alloc] init]; //StationPinView
pinPosition = CLLocationCoordinate2DMake([[aStation valueForKey:#"latitude"] doubleValue], [[aStation valueForKey:#"longitude"] doubleValue]);
stationPin.stationName = [aStation valueForKey:#"stationName"];
stationPin.stationPosition = pinPosition;
stationPin.stationLength = [aStation valueForKey:#"platformLength"];
[self.mapView addAnnotation:stationPin];
[stationPin release];
}
My PFAnnotation.h is:
#interface PFAnnotation : NSObject <MKAnnotation>
{
NSString *stationName;
CLLocationCoordinate2D stationPosition;
NSNumber *stationLength;
}
#property (nonatomic, retain) NSString *stationName;
#property CLLocationCoordinate2D stationPosition;
#property (nonatomic, retain) NSNumber *stationLength;
#end
and my PFAnnotation.m is:
#implementation PFAnnotation
#synthesize stationName;
#synthesize stationPosition;
#synthesize stationLength;
- (CLLocationCoordinate2D)coordinate;
{
return stationPosition;
}
- (NSString *)title
{
return stationName;
}
- (NSString *)subtitle
{
if (stationLength == nil)
return nil;
else
return [NSString stringWithFormat:#"Platform Length: %#ft",stationLength];
}
- (void)dealloc {
[stationName release];
[stationLength release];
[super dealloc];
}
I have read in some other threads that, setting the annotation properties from background thread is the reason for the above error. But in my case,it is not so because every thing is performed on the main thread. Please advise.
ok..solved it at last!!! I think it was due to the animation provided during addition of annotations. since there were a number of annotations that were added back-to-back with animation and also the annotations were removed just before the animation started, there could have been a reference to the released annotation(this is my guess). Moreover, the removal+addition process was made on each regionDidChangeAnimated call, which could have made a overlap between the removal and addition process. Anyway, how I solved it was that, I provided a timer which will be fired only after 1 second after every regionDidchangeAnimated to make sure that the user has done with dragging. Thus unnecessary addition+removal of annotations was avoided and I was able to avoid the crash. Thanks to all guys here for their time taken to support me, especially Guntis Treulands.
In your PFAnnotation class, did you declare both title and subtitle properties as they are in the protocol?
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKAnnotation_Protocol/Reference/Reference.html
If Your PFAnnotation really has incorrect setters getters for string values:
from here: http://cocoadevcentral.com/d/learn_objectivec/
Setter:
- (void) setCaption: (NSString*)input
{
[caption autorelease];
caption = [input retain];
}
Getter:
- (NSString*) caption
{
return caption;
}
Release:
- (void) dealloc
{
[caption release];
[super dealloc];
}
also - it's easier to provide coordinates in this way: (also works on ios 3.1.3)
stationPin.stationPosition = (CLLocationCoordinate2D) {[[aStation valueForKey:#"latitude"] doubleValue], [[aStation valueForKey:#"longitude"] doubleValue]}
than (only from ios 4)
stationPin.stationPosition = CLLocationCoordinate2DMake([[aStation valueForKey:#"latitude"] doubleValue], [[aStation valueForKey:#"longitude"] doubleValue]);
Please check whether an explicit removal of observer to the property "title" is being done anywhere in the code.
Edit:
Thanks #BlackFrog. I think I'm nearer now, but the values are still not get getting through...
The values are set as shown by logs within [progressController updateProgressSummary:...] but are nil when I log them in progressUpdate initWithProgressUpdate:.... as shown below.
I'm slightly confused over which property is used the one set for progressUpdate or the ones set for each of the 3 components of progressUpdate. I have changed the 3 individual properties from assign to retain as suggested and have also tried doing the same with the overall progressUpdate property too (not shown here).
progressController.h
......
#property (nonatomic, assign) ProgressUpdate *progressUpdate;
progressController.m
// Ask delegate to update and display Progress text
-(void) updateProgressSummary:(NSString *)summary detail:(NSString *)detail percentComplete:(NSNumber *)complete {
// These report the proper values
DLog(#"Reporting Summary - %s", [summary UTF8String]);
DLog(#"Reporting Detail - %s", [detail UTF8String]);
DLog(#"Reporting Complete - %i", [complete intValue]);
if (summary != nil)
self.progressUpdate.summaryText = summary;
self.progressUpdate.detailText = detail;
self.progressUpdate.percentComplete = complete;
ProgressUpdate *progressUpdateForIssue = [[ProgressUpdate alloc] initWithProgressUpdate:progressUpdate];
[self.delegate performSelectorOnMainThread:#selector(displayProgress:) withObject:progressUpdateForIssue waitUntilDone:NO];
[progressUpdateForIssue release];
}
But then a few milliseconds later...., inside the object....they're nil.
progressUpdate.h
.....
#property (nonatomic, retain) NSString *summaryText;
#property (nonatomic, retain) NSString *detailText;
#property (nonatomic, retain) NSNumber *percentComplete;
progressUpdate.m
-(id) initWithProgressUpdate:(ProgressUpdate *)update {
if ((self = [super init])) {
summaryText = [update.summaryText copy];
detailText = [update.detailText copy];
percentComplete = [[NSNumber alloc] initWithFloat:[update.percentComplete floatValue]];
}
// These report nil values
DLog(#"Reporting in progUpdate summaryText - %s", [summaryText UTF8String]);
DLog(#"Reporting in progUpdate detailText - %s", [detailText UTF8String]);
DLog(#"Reporting in progUpdate percentComplete - %i", [percentComplete intValue]);
return self;
}
end of update
I need some help with passing data in a custom class from one thread to another. Its there before the pass but then disappears upon arrival. I've tried everything I know, but to no avail.
My background thread calls ProgressController and passes it details of the current progress. That in turn does performSelectorOnMainThread on ProgressController's delegate (the view controller) to display the details.
It was all working fine when I was passing through a single NSString, but I need to pass two strings and a number and as performSelectorOnMainThread can only pass one object, I have encapsulated these in a custom object - ProgressUpdate.
The data gets through to ProgressController correctly but is null by the time that it appears in the View Controller. I know this as I've put NSLogs in various places.
I wonder if its to do with:
multithreading and custom objects
the fact that ProgressController is a singleton, which is why I have then alloc'd a new ProgressUpdate each time its called, but that has not helped.
Any ideas welcome. For clarity, the code is below.
ProgressUpdate.h
#import <Foundation/Foundation.h>
#interface ProgressUpdate : NSObject {
NSString *summaryText;
NSString *detailText;
NSNumber *percentComplete;
}
#property (nonatomic, assign) NSString *summaryText;
#property (nonatomic, assign) NSString *detailText;
#property (nonatomic, assign) NSNumber *percentComplete;
-(id) initWith:(ProgressUpdate *)update;
#end
ProgressUpdate.m
#import "ProgressUpdate.h"
#implementation ProgressUpdate
#synthesize summaryText, detailText, percentComplete;
-(id) initWith:(ProgressUpdate *)update {
self = [super init];
self.summaryText = update.summaryText;
self.detailText = update.detailText;
self.percentComplete = update.percentComplete;
return self;
}
#end
ProgressController.m
static ProgressController *sharedInstance;
+ (ProgressController *)sharedInstance {
#synchronized(self) {
if (!sharedInstance)
[[ProgressController alloc] init];
}
return sharedInstance;
}
+(id)alloc {
#synchronized(self) {
NSAssert(sharedInstance == nil, NSLocalizedString(#"Attempted to allocate a second instance of a singleton ProgressController.", #"Attempted to allocate a second instance of a singleton ProgressController."));
sharedInstance = [super alloc];
}
return sharedInstance;
}
-(id) init {
if (self = [super init]) {
[self open];
}
return self;
}
.........
// Ask delegate to update and display Progress text
-(void) updateProgressSummary:(NSString *)summary detail:(NSString *)detail percentComplete:(NSNumber *)complete {
if (summary != nil)
self.progressUpdate.summaryText = summary;
self.progressUpdate.detailText = detail;
self.progressUpdate.percentComplete = complete;
ProgressUpdate *progressUpdateForIssue = [[ProgressUpdate alloc] initWith:progressUpdate];
[self.delegate performSelectorOnMainThread:#selector(displayProgress:) withObject:progressUpdateForIssue waitUntilDone:NO];
[progressUpdateForIssue release];
}
RootViewController.m
// Delegate method to display specific text in Progress label
- (void) displayProgress:(ProgressUpdate *)update {
[progressSummaryLabel setText:update.summaryText];
[progressDetailLabel setText:update.detailText];
[progressBar setProgress:[update.percentComplete intValue]];
[progressView setNeedsDisplay];
}
In the init method, you are only assigning the ivars and not retaining them in the new object.
Redo your init method as the following:
-(id) initWithProgressUpdate:(ProgressUpdate *)update {
if ((self = [super init])) {
summaryText = [update.summaryText copy];
detailText = [update.detailText copy];
percentComplete = [[NSNumber alloc] initWithFloat:[update.percentComplete floatValue];
}
return self;
}
Couple of points:
You should not use accessor in the init method
Rename your init method to be a lot clear
In the #property, change the assign to retain
Try removing the statement '[progressUpdateForIssue release];' in the method
'-(void) updateProgressSummary:(NSString *)summary detail:(NSString *)detail percentComplete:(NSNumber *)complete '.
Also change the property attribute from 'assign' to 'retain' in your class ProgressUpdate.
You could release those properties in the dealloc method .
Good luck.
I want to have one object that is initialized in the delegate and I want to be able to use this object anywhere across view controllers (doesn't depend on what view I am currently at). I am guessing the solution to this would be to have a singleton class, so far I have the following:
#interface LocationManager : NSObject <CLLocationManagerDelegate>{
NSDate *enter;
NSDate *exit;
CLLocationManager * manager;
}
#property (nonatomic, retain) NSDate * enter;
#property (nonatomic, retain) NSDate * exit;
- (BOOL)registerRegionWithLatitude:(double)latitude andLongitude:(double)longitude;
+ (LocationManager *)instance;
#end
#import "LocationManager.h"
#implementation LocationManager
#synthesize enter;
#synthesize exit;
#pragma mark - CLLocationManager delegate
static LocationManager *gInstance = NULL;
+ (LocationManager *)instance
{
#synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
#end
Is this correct? So all I need to do to access this is just to call instance? Inside LocationManager I also want to have only one CLLocationManager, called manager.. however, where do I initialize it so I only have one? Can I do the following? Most other singleton examples doesn't have any variables in the class, so that's where I got confused
+ (LocationManager *)sharedLocationManager
{
#synchronized(self)
{
if (lm == NULL){
lm = [[self alloc] init];
lm.manager = [[CLLocationManager alloc] init];
lm.manager.delegate = lm;
}
}
return(lm);
}
Basically -- yes.
Just a couple of small things:
static LocationManager *gInstance = NULL;
instead of NULL, you should use nil, it's a convention in Objective-C.
You should also overwrite alloc, new, copyWithZone:, and mutableCopyWithZone:. From Buck/Yacktman: "Cocoa Design Patterns", p. 153:
+ (id)hiddenAlloc
{
return [super alloc];
}
+ (id)new
{
return [self alloc];
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone *)zone
{
[self retain];
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [self copyWithZone:zone];
}
This way, your singleton object cannot be copied. You need to call hiddenAlloc from your instance method (by the way, the method to access a Singleton object is often called sharedInstance in Objective-C).
For other singleton styles with their pros and cons, check out this question.
Personally, I prefer this style (copied from one of the answers on that link):
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
In fact, there's a tried-and-true method to create singletons already. Download the SynthesizeSingleton.h file (from a Cocoa with Love article). It contains a massive amount of pre-processor code which will generate any singleton for you. Hit the article for more details.
Since the factory method "instance" is a class-level method, the #synchronized block should be
#synchronized([LocationManager class]) {
//}
Hi I got some problem with NSOperation .
I always got error at self = [super init]; (already use break point to find this)
it always return "Program received signal: EXC_BAD_ACCESS" all the time
//AddThread.h
#interface AddThread : NSOperation
{
NSString * str;
}
#property (nonatomic,retain) NSString * str;
-(id) initWithString:(NSString *) tmpStr;
#end
and for .m
//AddThread.m
#import "AddThread.h"
#implementation AddThread
#synthesize str;
- (id) initWithString:(NSString *)tmpStr
{
self = [super init];
if (self != nil)
{
self.str = tmpStr;
}
//NSLog(self);
//[super init];
return self;
}
- (void) main
{
NSLog(self.str);
}
- (void) dealloc{
[str release];
str = nil;
[super dealloc];
}
#end
well I stuck with this for while and if possible any resources ,articles things for basic example of NSoperation?
In your main method, you are calling NSLog(self.str) - While this will work if the object you pass in is a string, it won't work if you continue to try and log other objects. NSLog takes a format string as a parameter. If you just do NSLog(self) like you are in some of your commented code, and self is not a string, it'll crash because it expected a string. You should do NSLog(#"self: %#", self) the %# will print out the string returned by an objects description method.
Other than that, your init method looks fine, how exactly are you creating an instance of this object? Could you show the code for that? The problem may lie there.