Expanding my singleton class - objective-c

I have use a singleton class that contained just one passed value. I then tried to add another one.
#import <Foundation/Foundation.h>
#interface GlobalValueContainer : NSObject {
NSString *passedText;
NSString *myPassedPictureName;
}
#property (nonatomic, strong) NSString* passedText;
#property (nonatomic, strong) NSString* myPassedPictureName;
+ (GlobalValueContainer *) sharedStore;
#end
#import "GlobalValueContainer.h"
#implementation GlobalValueContainer;
#synthesize passedText;
#synthesize myPassedPictureName;
static GlobalValueContainer *sharedStore = nil;
+ (GlobalValueContainer *) sharedStore {
#synchronized(self){
if (sharedStore == nil){
sharedStore = [[self alloc] init];
}
}
return sharedStore;
}
#end
From the first view I then try to set the myPassedPictureName
-(IBAction)setPicture:(id)sender{
myPicture = #"Hus";
GlobalValueContainer* localContainer = [GlobalValueContainer sharedStore];
localContainer.myPassedPictureName = myPicture;
}
and on the second view I want to set an imageview with that name (+png that is)
- (void)viewDidLoad
{
[super viewDidLoad];
//Store* myStore = [Store sharedStore];
GlobalValueContainer* localContainer = [GlobalValueContainer sharedStore];
myPassedPictureName = localContainer.myPassedPictureName;
myPicture.image = [UIImage imageNamed:myPassedPictureName];
whatFile.text = myPassedPictureName;
//object.imageView.image = [UIImage imageNamed:#"downloadedimage.png"];
}
the picture doesnt show. I have also tried to add a UIlabel and set the string that should have been passed. But it also turns out blank.
When I pass text using the "passedText" it works fine. When I added the second NSString, nothing happens?.
First things first. Can anyone see what´s wrong (still learning obj c here :) and, is it the correct way I try to manipulate an UIImageView. I want to use the myPassedPictureName to set a picture on a number of UIViews depending on the button being pressed.
Looking forward to your input.

I'm quite sure using singleton for passing value like this is not a good idea.Singleton is not designed for passing value,but for doing something.So you can not use Property like
sharedManager.passValue
Here has some good discussion about singleton.
When should you use the singleton pattern instead of a static class?
and
What should my Objective-C singleton look like?
So I suggest write it like this:
#import <Foundation/Foundation.h>
#interface GlobalValueContainer : NSObject {
}
+ (id) sharedManager;
-(NSString*)myText;
-(NSString*)myPictureName;
#end
#import "GlobalValueContainer.h"
#implementation GlobalValueContainer;
static GlobalValueContainer *sharedManager = nil;
+ (id) sharedManager {
#synchronized(self){
if (sharedManager == nil){
sharedManager = [[self alloc] init];
}
}
return sharedManager;
}
-(NSString*)myText
{
return #"your text";
}
-(NSString*)myPictureName
{
return #"yourPictureName.png";
}
#end

If variable "myPassedPictureName" is URL to file then you cannot use this method for it:
[UIImage imageNamed:myPassedPictureName];
you should use
NSData *data = [[NSData alloc] initWithContentsOfURL:myPassedPictureName];
UIImage *image = [[UIImage alloc] initWithData:data];

Related

Information in text fields do not get retained objective c

This is a simple program that takes two numbers inputed by the user in two separate text fields and adds them together when the 'Add' button is clicked. The way I have done this is by subclassing NSTextField into a class called MyTextField. I believe my error has something to do with memory leaks, but being an inexperienced programmer, I'm not quite sure how to deal with allocating and deallocating memory.
The problem is: When I click the Add button, it works perfectly. However, when I click it again, it does not show the correct amount. It keeps taking my inputs as nil and outputting 0. I have attached my code.
Note: I know that this is much more complicate than it needs to be! I am simply showing a simpler version of a much more complicated program that displays the exact same error.
MyTextField.h
#import <Cocoa/Cocoa.h>
#interface MyTextField : NSTextField
#property (strong) NSString* number;
#property double dblNum;
-(id)initWithNumber:(NSString *)number;
-(NSString *)getNumber;
-(void)setNumber;
-(double)getDblNum;
-(void)setDblNum;
-(double)calcSum:(MyTextField *)other;
-(NSString *)description;
#end
MyTextField.m
#import "MyTextField.h"
#implementation MyTextField
-(id)initWithNumber:(NSString *)number{
self = [super init];
if (self) {
if ([number length] > 0){
_number = number;
}else{
_number = #"0";
}
[self setDblNum];
}
return self;
}
- (id)init {
return [self initWithNumber:[self getNumber]];
}
-(NSString *)getNumber{
return _number;
}
-(void)setNumber{
_number = [self stringValue];
}
-(double)getDblNum{
return _dblNum;
}
-(void)setDblNum{
_dblNum = [_number doubleValue];
}
-(double)calcSum:(MyTextField *)other{
return [self getDblNum] + [other getDblNum];
}
- (NSString *)description{
return [NSString stringWithFormat: #"Number: %f", _dblNum];
}
#end
ViewController.h
#import <Cocoa/Cocoa.h>
#import "MyTextField.h"
#interface ViewController : NSViewController
#property (strong) IBOutlet MyTextField *valueOne;
#property (strong) IBOutlet MyTextField *valueTwo;
#property (strong) IBOutlet NSTextField *answer;
- (IBAction)btnAdd:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "MyTextField.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
}
- (IBAction)btnAdd:(id)sender {
_valueOne = [[MyTextField alloc] initWithNumber:[_valueOne stringValue]];
_valueTwo = [[MyTextField alloc] initWithNumber:[_valueTwo stringValue]];
double ans = [_valueOne calcSum:_valueTwo];
_answer.stringValue = [NSString stringWithFormat:#"%.2f", ans];
}
#end
P.S. Sorry for the annoying amount of code. Thanks!
Your btnAdd: is where the problem is.
- (IBAction)btnAdd:(id)sender {
_valueOne = [[MyTextField alloc] initWithNumber:[_valueOne stringValue]];
_valueTwo = [[MyTextField alloc] initWithNumber:[_valueTwo stringValue]];
double ans = [_valueOne calcSum:_valueTwo];
_answer.stringValue = [NSString stringWithFormat:#"%.2f", ans];
}
Each time the button is tapped, you're recreating both MyTextField instances. Instead, you simply need to get & set the values from your existing valueOne and valueTwo outlets, like:
- (IBAction)btnAdd:(id)sender {
double value1 = [_valueOne.text doubleValue];
double value2 = [_valueTwo.text doubleValue];
double ans = value1 + value2;
_answer.stringValue = [NSString stringWithFormat:#"%.2f", ans];
}
Obviously, you need to check the validity of the valueOne and valueTwo text strings.
You really don't need a MyTextField subclass, because it's not doing anything except converting strings to doubles (and vice versa).

NSMutableArray resetting itself when WindowDidLoad is done

When I pass a NSMutableArray from a controller class to a NSWindowController class using #property and #synthesize I am able to use the objects of the array in the windowDidLoad method.
However, after the method is done and I click a button on the window triggerig an IBAction, the passed value is nil.
Can anyone explain me why this is happening and how I can preserve the NSMutableArray?
Here is the code:
passClass.h
#import <Foundation/Foundation.h>
#class ResultWindowController;
#interface passClass : NSObject {
#private
IBOutlet NSTextField *searchField;
ResultWindowController *resultWindowController;
}
- (IBAction)passIt:(id)sender;
#end
passClass.m
#import "passClass.h"
#import "ResultWindowController.h"
#implementation passClass
- (IBAction)passIt:(id)sender {
NSString *searchString = searchField.stringValue;
NSMutableArray array = [[NSMutableArray alloc]init];
[array addObject:searchString];
[array addObject:searchString];
if(!resultWindowController) {
resultWindowController = [[ResultWindowController alloc] initWithWindowNibName:#"ResultWindow"];
resultWindowController.array =[[NSMutableArray alloc]initWithArray:array copyItems:YES];
[resultWindowController showWindow:self];
}
}
#end
ResultWindowController.h
#import <Cocoa/Cocoa.h>
#interface ResultWindowController : NSWindowController <NSTableViewDataSource> {
IBOutlet NSTableView *resultView;
NSMutableArray *resultList;
//NSMutableArray *array;
}
- (IBAction)returnValue:(id)sender;
#property (nonatomic,strong) NSMutableArray *array;
#end
ResultWindowController.m
#import "Results.h"
#interface ResultWindowController ()
#end
#implementation ResultWindowController
//#synthesize array;
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
resultList = [[NSMutableArray alloc] init];
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
for (NSInteger i = 0; i< [array count];i++)
{
Results *result = [[Results alloc]init];
result.resultName = [self.array objectAtIndex:i];
[resultList addObject:result];
[resultView reloadData];
NSLog (#"self.array: %#", self.array);
// works fine, tableview gets populated, array is correct
}
}
- (NSInteger) numberOfRowsInTableView:(NSTableView *)resultView{
return [resultList count];
}
- (id)tableView:(NSTableView *)resultView objectValueForTableColumn:(NSTableColumn *)resultColumn row:(NSInteger)row{
Results *result = [resultList objectAtIndex:row];
NSString *identifier = [resultColumn identifier];
return [result valueForKey:identifier];
}
- (IBAction)selectedSeries:(id)sender {
NSLog (#"self.array: %#", self.array);
//when I break here the array is nil
}
#end
Here is the NSLog result:
2013-12-26 10:36:49.487 MyProgram[545:303] self.array: (
"test",
"test"
)
2013-12-26 10:37:24.044 MyProgram[545:303] self.array: (null)
Try to remove NSMutableArray *array; from ResultWindowController class declaration and leave only the property declaration for it.
Or you could try initiating the array property in your ResultVWindowsContorller init class and in the - (IBAction)passIt:(id)sender just add objects to array.
I honestly can't see how this works at all unless, in -windowDidLoadNib you are expecting array to be empty.
When you synthesise a property, the default name of the instance variable that is used is prefixed by an underscore. Thus the class in your code has two instance variables, array and _array.
There are several ways to fix this. Here's what I think what you should do is delete the instance variable in your interface definition. Then you'll start getting compilation errors every for each time you use it. Fix them by using the property instead, so for example, the line
result.resultName = [array objectAtIndex:i];
in -windowDidLoadNib becomes
result.resultName = [self.array objectAtIndex:i];

Why can't I populate my controller with items?

I'm using an ItemController to provide a list of items to use in a tableview. I can't seem to populate the controller though, and I'm not sure why.
Here's the code for the controller class:
.h
#import <Foundation/Foundation.h>
#class Item;
#interface ItemController : NSObject
#property (nonatomic, copy) NSMutableArray *items;
- (NSUInteger)countOfList;
- (Item*)objectInListAtIndex:(NSUInteger)theIndex;
- (void)addItem:(Item *)item;
#end
.m
#import "ItemController.h"
#import "Item.h"
#interface ItemController ()
#end
#implementation ItemController
- (NSUInteger)countOfList {
return [self.items count];
}
- (Item *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.items objectAtIndex:theIndex];
}
- (void)addItem:(Item *)item {
[self.items addObject:item];
}
#end
Item.m
#implementation Item
-(id)initWithName:(NSString *)name{
self = [super init];
if (self) {
_name = name;
return self;
}
return nil;
}
#end
I'm using the following code to populate the list:
ItemController* controller = [[ItemController alloc] init];
for (NSString* key in raw_data) {
NSLog(key); // This outputs the keys fine
[controller addItem:[[Item alloc] initWithName:key]];
}
NSLog([NSString stringWithFormat:#"%d",[controller countOfList]]); // Always 0
You need to initialize the array in the init methond.
- (id)init {
self = [super init];
if (self) {
self.items = [[NSMutableArray alloc] init];
}
return self;
}
You need to initialize your variable items. In your init method, call self.items = [NSMutableArray new]; and also change your array property from copy to retain.
I also believe your class ItemController should be of kind UIViewController and not NSObject.
#interface ItemController : UIViewController
You don't initialise the _items instance variable anywhere, so it's always nil. The result of any integer-returning method called on nil will be 0, so you see that the count is 0.

Singleton not updating variable immediately

I have a singleton here is the header file:
#import <Foundation/Foundation.h>
#interface Shared : NSObject
{
NSString *messages;
}
#property (nonatomic, retain) NSString *messages;
+ (Shared*)sharedInstance;
#end
Here is the implementation:
#import "Shared.h"
static Shared* sharedInstance;
#implementation Shared
#synthesize messages;
+ (Shared*)sharedInstance
{
if ( !sharedInstance)
{
sharedInstance = [[Shared alloc] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if ( self )
{
messages = [[NSString alloc] init];
}
return self;
}
#end
The problem is when the I use
[Shared sharedInstance].messages = someVariable;
I can use
NSLog([Shared sharedInstance].messages);
and it shows the right output, but when i check from another class, NSLog doesn't show any output. I have the NSLog in the viewDidLoad method of another class, so when I click a button to go to the next view, it should output the value of the string, but it only works the second time. If the variable is set to dog, first it outputs nothing, then when I close the view and try again, it outputs dog. however, if I then change the variable to cat, it will output dog, and on the next attempt, output cat. I want it to update immediately, rather than remain one behind all the time.
EDIT: Here's the code from the other classes
This particular section is from a view controller class in the method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Omitted, just preparing the DB, and emptying the array.
if ([db open])
{
FMResultSet *s = [db executeQueryWithFormat:#"SELECT ShabadID FROM Shabad WHERE Gurmukhi LIKE %#", currentLine];
while ([s next])
{
lineID = [s intForColumn:#"ShabadID"];
}
s = [db executeQueryWithFormat:#"SELECT Gurmukhi, ShabadID FROM Shabad WHERE ShabadID LIKE %i", lineID];
while ([s next])
{
//NSLog([s stringForColumn:#"Gurmukhi"]);
[paragraphArray addObject:[s stringForColumn:#"Gurmukhi"]];
}
Text = #"";
for (int i = 0; i<[paragraphArray count]; i++)
{
Text = [Text stringByAppendingFormat:#"%#\n", [paragraphArray objectAtIndex:i]];
}
[Shared sharedInstance].messages = Text;
}
Then in the another class, where I want the text to appear, in the viewDidLoad method,
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog([Shared sharedInstance].messages);
UITextView *myUITextView = [[UITextView alloc] initWithFrame:CGRectMake(0,30,310,450)];
myUITextView.text = [Shared sharedInstance].messages;
myUITextView.textAlignment = NSTextAlignmentCenter;
myUITextView.textColor = [UIColor blackColor];
myUITextView.font = [UIFont fontWithName:#"GurbaniLipiLight" size:24];
[myUITextView setBackgroundColor:[UIColor clearColor]];
myUITextView.editable = NO;
myUITextView.scrollEnabled = YES;
[ScrollerView addSubview:myUITextView];
}
Sure the NSLog doesn't show up right, but neither does the text in the textview, it does the same thing the NSLog does.
There is an assumption here about what order things happen in that's not quite right. Assuming there's a segue involved in this, didSelectRowAtIndexPath: is called after the new view controller is prepared but before it's displayed. Moving code to viewWillAppear: or viewDidAppear: delays execution until after the calling controller has set new data.
The other approach for communication between controllers that use a segue, is to use prepareForSegue: in the first controller to set data that the second controller needs. That way it should be available when the view is loaded.

"Incomplete implementation" warning in XCode 4.0

This application is rewritten code from the Cococa and Objective C Up and Running book.
As I try to understand everything in the beginning, I would like to know, where I made a mistake, in the code below. To me, everything looks fine.
Could you, therefore, help me identify the source of the warning:
Incomplete Implementation
I got this in the #implementation Photo line in Photo.m source code file?
Photo.h
#import <Foundation/Foundation.h>
#interface Photo : NSObject{
NSString* caption;
NSString* photographer;
}
+ (Photo*) photo;
- (NSString*) caption;
- (NSString*) photographer;
- (void) setCaption: (NSString*)input;
- (void) setPhotographer: (NSString*)input;
#end
Photo.m
#import "Photo.h"
#implementation Photo // <- Incomplete Implementation?
- (id)init
{
self = [super init];
if (self) {
[self setCaption:#"Default Caption"];
[self setPhotographer:#"Default Photographer"];
}
return self;
}
+ (Photo*) caption {
Photo* newPhoto = [[Photo alloc] init];
return [newPhoto autorelease];
}
- (NSString*) caption {
return caption;
}
- (NSString*) photographer {
return photographer;
}
- (void) setCaption:(NSString *)input {
[caption autorelease];
caption = [input retain];
}
- (void) setPhotographer: (NSString *)input {
[photographer autorelease];
photographer = [input retain];
}
- (void)dealloc
{
[self setCaption:nil];
[self setPhotographer:nil];
[super dealloc];
}
#end
I use Snow Leopard 10.6.7 and Xcode 4.0.0.
Unless its a typo, your Class method defined as + (Photo*) Photo; is not implemented (there is a + (Photo*) Caption {} method which looks its just an accident.
Edit: A simpler way to do have this functionality is to use properties, which are a shortcut that create the getter and setter for a variable for us, (see this link for a good beginner's tutorial: iPhone 101) for your instance variables like so:
in your .h file:
#interface Photo : NSObject{
NSString* caption;
NSString* photographer;
}
#property (nonatomic, retain) NSString *caption;
#property (nonatomic, retain) NSString *photographer;
#end
in your .m file:
#implementation Photo
#synthesize caption, photographer;
//Other stuff (init and any custom methods for class etc.. NOT getters and setters for variables)
- (void)dealloc
{
[caption release];
[photographer release];
[super dealloc];
}
You are receiving this error because in your header file you declared that there would be a method:
+ (Photo*) photo;
but you didn't implement it in the m file.
EDIT:
It looks like this:
+ (Photo*) caption {
Photo* newPhoto = [[Photo alloc] init];
return [newPhoto autorelease];
}
should be:
+ (Photo*) photo {
Photo* newPhoto = [[Photo alloc] init];
return [newPhoto autorelease];
}
In general, when you mouse over the warning, it will not tell you which method it is missing, but there are at least two other ways to get this information:
Type Cmd-4 or select the Issue Navigator view (the ! in a triangle icon), then expand the "Semantic Issue" warning for this issue. You will then see a message to the effect of "Method definition for "" not found.
Type Cmd-7 or select the Log View (the rightmost icon that looks like a caption bubble), then select the appropriate issue from the list. You will see the same message.
You are missing +photo because you accidentally typed caption:
+ (Photo*) caption {
Photo* newPhoto = [[Photo alloc] init];
return [newPhoto autorelease];
}
should be
+ (Photo*) photo {
Photo* newPhoto = [[Photo alloc] init];
return [newPhoto autorelease];
}
Your .m file does not have the implementation for:
+ (Photo*) photo;
That's the missing method.
try changing
+ (Photo*) caption {
Photo* newPhoto = [[Photo alloc] init];
return [newPhoto autorelease];
}
to
+ (Photo*) photo {
Photo* newPhoto = [[Photo alloc] init];
return [newPhoto autorelease];
}