I have one model.
I want to update one attribute each 3 seconds after init, and I need to change the attribute in one other view controller later.
the code is:
MODEL
#interface Ap : NSObject
#property (nonatomic, retain) NSString *address;
#property (nonatomic, retain) NSString *sessionId;
+ (id)sharedInstance;
#end
#implementation Ap
#synthesize sessionId, address;
-(id) init {
if (self = [super init]) {
self.address = nil;
self.sessionId = nil;
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(heartbeat) userInfo:nil repeats:YES];
}
return self;
}
-(void) updateSession{
sessionId = #"some string";
}
- (void) update{
self.sessionId = #"some value from network";
}
+ (Ap *)sharedInstance {
if (!sharedInstance) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
#end
CONTROLLER
- (void) viewDidLoad {
[super viewDidLoad];
[[Ap sharedInstance] updateSession];
}
The error is: (lldb) bad access when model update
And I change the updateSession method to
-(void) updateSession{
self.sessionId = #"some string";
}
The error is gone, can anyone tell me why?
You should use self.ap = [[Ap alloc] init]; in your app delegate. Your model object might be getting released when you try to call update on that which might cause a crash.
Your code will look like this,
#interface SomeAppDelegate : UIResponder{
#property(nonatomic, retain) Ap *ap; //declare property here
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if(!self.ap){
self.ap = [[Ap alloc] init]; //use property here as self.ap which will retain ap. Otherwise it will not retain it.
}
}
Update:
Looks like you have removed the previous question and added a completely new question to the previous one. Anyways I will try to answer this one as well.
The error is gone, can anyone tell me why?
The reason is same as what I mentioned above. If you use self.sessionId = #"some string";, you are retaining the object. Because it is a property and a property will internally retain since you have declared that property as retain. But if you use sessionId = #"some string";, its scope is only inside that method since you are not using the property. You are directly setting the value without calling property and it will autoreleased after that method. And hence you will get a bad access.
I would suggest you to go through the apple documentation to understand more about properties and its working.
Related
I have a iPad application where I'm attempting to use a singleton. This is the code in the .h file:
//-------------------------------------------
//-- singleton: timeFormat
#interface SingletonTimeFormat : NSObject {
}
#property (nonatomic, retain) NSNumber *timeFormat;
+ (id)sharedTimeFormat;
#end
This is the code from the .m file:
//-------------------------------------------
//-- SingletonTimeFormat
#implementation SingletonTimeFormat {
}
#synthesize timeFormat;
//-- sharedColorScheme --
+ (id)sharedTimeFormat {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-id) init {
self = [super init];
if (self) {
timeFormat = [[NSNumber alloc] init];
}
return self;
}
#end
I load the value (either 12 or 24) in AppDelegate - didFinishLaunchingWithOptions, then when I want to get the value of timeFormat I use this:
SingletonTimeFormat *stf = [[SingletonTimeFormat alloc]init];
if([stf.timeFormat isEqualToNumber: [NSNumber numberWithInt:12]]) {
which returns 0 (it was set correctly in AppDelegate, but apparently when I do the alloc in another class, it loses it's value. So obviously it's not working! (I have several other singletons that have the same pattern, but so far they appear to be working.
What's wrong here and how do I fix it?
You don't want to call your singleton using alloc init. With this singleton, all references to it should be through its sharedTimeFormat method, which will init the object if necessary, and will return the singleton instance otherwise.
In other words, it doesn't appear that you're referencing the instance of the object stored in the static sharedObject variable, which means that it's stored value will not necessarily be the same.
I've encountered a stupid problem, and I've tried almost everything (bought 3 books, went through the whole google :)) but nothing helped. And it seems to me like the solution should be extremely simple...
I need to declare a singleton in Objective-C (for an iOS app, if that matters), and it should have some properties that I need to update from other classes. But I can't do that - the properties just won't update, they have the same values set in the "init" method.
I've created a simple app to test out this problem. That's what I've done:
First, I've declared a sample class and its subclass that I'm going to use as a singleton's property:
#interface Entity : NSObject
#property (nonatomic, strong, readwrite) NSMutableString * name;
#end
#implementation Entity
#synthesize name;
#end
#interface Company : Entity
#property (nonatomic, strong, readwrite) NSMutableString * boss;
#property (nonatomic) int rating;
#end
#implementation Company
#synthesize boss, rating;
#end
Then I declare the singleton itself based on the method described in the "iOS Programming Guide by Big Nerd Ranch" book. I'm using both my custom class and a standard NSMutableString as properties, just for clarity's sake:
#class Company;
#interface CompanyStore : NSObject
{
NSMutableString * someName;
}
#property (nonatomic, strong, readwrite) Company * someCompany;
#property (nonatomic, strong, readwrite) NSMutableString * someName;
+ (CompanyStore *) store;
- (void) modifyCompanyProperties;
#end
#implementation CompanyStore
#synthesize someCompany, someName;
// Declaring the shared instance
+ (CompanyStore *) store
{
static CompanyStore * storeVar = nil;
if (!storeVar) storeVar = [[super allocWithZone:nil] init];
return storeVar;
}
// Replacing the standard allocWithZone method
+ (id) allocWithZone:(NSZone *)zone
{
return [self store];
}
Then I initialize all the properties with initial values:
- (id) init
{
self = [super init];
if (self) {
someCompany = [[Company alloc] init];
[someCompany setBoss:[NSMutableString stringWithFormat:#"John Smith"]];
[someCompany setName:[NSMutableString stringWithFormat:#"Megasoft"]];
[someCompany setRating:50];
someName = [[NSMutableString alloc] initWithString:#"Bobby"];
}
return self;
}
And from another class (view controller that displays the contents in a view):
1. I get the values of the singleton's properties. Everything's okay - I get "John Smith", "Megasoft", "Bobby" and 50 for my int value. The values from my init method.
2. I change the singleton's properties from that view controller (using several ways - I'm not sure now which one is right):
- (IBAction)modify2Button:(id)sender {
CompanyStore * cst = [CompanyStore store];
NSMutableString * name = [[NSMutableString alloc] initWithString:#"Microcompany"];
NSMutableString * boss = [[NSMutableString alloc] initWithString:#"Larry"];
[[[CompanyStore store] someCompany] setName:name];
cst.someCompany.boss = boss;
NSMutableString * strng = [[NSMutableString alloc] initWithString:#"Johnny"];
[cst setSomeName:strng];
}
... and then I'm trying to get the values again. I'm still getting the old set - "John Smith", "Megasoft" etc. even though when I set a breakpoint at one of the strings, I can see that singleton's name property is "Microcompany" and not "Megasoft" at the time of the break... But it doesn't seem to be assigned.
3. Then I'm trying another thing - I'm calling from the view controller a singleton's private method, which assigns another set of values to the properties. This is that method in the singleton:
- (void) modifyCompanyProperties
{
NSMutableString * boss = [[NSMutableString alloc] initWithString:#"George"];
NSMutableString * name = [[NSMutableString alloc] initWithString:#"Georgeland"];
[someCompany setBoss:boss];
[someCompany setName:name];
[someCompany setRating:100000];
[someName setString:#"Nicholas"];
}
4. I'm trying to get the updated property values from the view controller again... and still get those "John Smith", "Megasoft"... Nothing changes.
It seems like the properties of the singleton are set only once and then I can't change them, even though their attributes are declared as "readwrite".
It looks like I don't understand something simple.
If someone could explain how to correctly declare and update properties in singletons, I would be very grateful.
First thing I noticed was that you are declaring "storeVar" in the body of the store method. And this looks like terribly wrong to me because every time you call this you'll re-initialize the singleton. You should declare the variable like this:
static CompanyStore * storeVar = nil;
#implementation CompanyStore
#synthesize someCompany, someName;
// Declaring the shared instance
+ (CompanyStore *) store
{
if (!storeVar) storeVar = [[super allocWithZone:nil] init];
return storeVar;
}
Also your init method is not exactly complete because you don't want to call init again after the singleton has been initialized so you have to check this and if it has been initialized you should simply return it:
- (id) init
{
if (storeVar!=nil) {
return storeVar;
}
self = [super init];
if (self) {
someCompany = [[Company alloc] init];
[someCompany setBoss:[NSMutableString stringWithFormat:#"John Smith"]];
[someCompany setName:[NSMutableString stringWithFormat:#"Megasoft"]];
[someCompany setRating:50];
someName = [[NSMutableString alloc] initWithString:#"Bobby"];
}
return self;
}
Also, this is not a mistake, just a mere suggestion - you can ditch #synthesize because since ios 6 because the compiler generates it automatically. But again, not a mistake to use it. Hope it helps
I created a simple UIViewController with a custom init method like this:
-(id)initWithGroupNumber:(NSString *)groupNumber {
if ((self = [super init]) == nil) {
return nil;
}
self.levelGroup = groupNumber;
return self; }
levelGroup is a property written in the .h file
#property (nonatomic, retain) NSString *levelGroup;
When I call the method above this way:
LevelsViewController *lvc = [[LevelsViewController alloc]initWithGroupNumber:#"5"];
the controller is allocated but all the property inside are set to nil. I can't understand why.
First of all when you deal with classes that have subclass of type mutable (e.g. NSMutableString), use copy.
So, your property should become:
#property (nonatomic, copy) NSString *levelGroup;
Then, inside the UIViewController synthesize the property
#synthesize levelGroup;
and in init do the following:
-(id)initWithGroupNumber:(NSString *)groupNumber {
if (self = [super init]) {
levelGroup = [groupNumber copy];
}
return self;
}
As written in the memory management guide you should not use self. inside init and in dealloc.
Use your property self.levelGroup to get or set the value.
Remember to release in dealloc:
[levelGroup release];
Hope it helps.
Replace your
self.levelGroup = [[NSString alloc]init];
with
self.levelGroup = groupNumber; // actually uses your init value.
After many hours wasted, I officially turn to the experts for help!
My problem lies with using a NSMutableArray as an instance variable, and trying to both add objects and return the array in a method in my class. I am obviously doing something fundamentally wrong and would be grateful for help...I have already tried all the suggestions from other similar questions on stackoverflow, read apples documentation, and basically all combinations of trial and error coding I can think of. The mutable array just alway returns (null). I've even tried creating properties for them, but still the array returns (null) and then I also am running into memory management problems due to the retain while setting the property, and the init in the init method for the class.
Here is what I am trying to do:
1) Loop through a series of UISwitches and if they are 'switched on', add a string to the NSMutableArray
2) Assign this mutable array to another array in another method
Any help much appreciated,
Andy
And for some code...
fruitsViewController.h
#import <UIKit/UIKit.h>
#interface fruitsViewController : UIViewController
{
NSMutableArray *fruitsArr;
UISwitch *appleSwitch;
UISwitch *orangeSwitch;
}
#property (nonatomic,retain) NSMutableArray *fruitsArr; // ADDED ON EDIT
#property (nonatomic,retain) IBOutlet UISwitch *appleSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *orangeSwitch;
- (IBAction)submitButtonPressed:(id)sender;
#end
fruitsViewController.m
#import "fruitsViewController.h"
#implementation fruitsViewController
#synthesize fruitsArr; // ADDED ON EDIT
#synthesize appleSwitch, orangeSwitch;
/* COMMENTED OUT ON EDIT
-(id)init
{
if (self = [super init]) {
// Allocate memory and initialize the fruits mutable array
fruitsArr = [[NSMutableArray alloc] init];
}
return self;
}
*/
// VIEW DID LOAD ADDED ON EDIT
- (void)viewDidLoad
{
self.fruitsArr = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
self.fruitsArr = nil;
self.appleSwitch = nil;
self.orangeSwitch = nil;
}
- (void)dealloc
{
[fruitsArr release];
[appleSwitch release];
[orangeSwitch release];
[super dealloc];
}
- (IBAction)submitButtonPressed:(id)sender
{
if ([self.appleSwitch isOn]) {
[self.fruitsArr addObject:#"Apple"; // 'self.' ADDED ON EDIT
}
if ([self.orangeSwitch isOn]) {
[self.fruitsArr addObject:#"Orange"; // 'self.' ADDED ON EDIT
}
NSLog(#"%#",self.fruitsArr); // Why is this returning (null) even if the switches are on?!
[fruitsArr addObject:#"Hello World";
NSLog(#"%#",self.fruitsArr); // Even trying to add an object outside the if statement returns (null)
}
#end
It seems like your init function is never called. If you're initializing this view controller from a NIB, you need to use initWithCoder. If not, just declare your fruitsArr in viewDidLoad.
Use view did load instead of init...
- (void)viewDidLoad
{
fruitsArr = [[NSMutableArray alloc] init];
}
Change that init for viewDidLoad and see what happens
Is your init method ever being called (in complicationsViewController). Add a NSLog to check this, you might be calling initWithNib: maybe.
At viewDidUnload you should remove self.fruitsArr = nil;, or, if you want to keep it, then initialize the fruitsArr in viewDidLoad (and remove it from init).
because fruitsArr don't be init.
you should do this first:
fruitsArr = [[NSMutableArray alloc] init];
so, I think you don't run - (id)init before you use fruitsArr.
If I have a custom class called Tires:
#import <Foundation/Foundation.h>
#interface Tires : NSObject {
#private
NSString *brand;
int size;
}
#property (nonatomic,copy) NSString *brand;
#property int size;
- (id)init;
- (void)dealloc;
#end
=============================================
#import "Tires.h"
#implementation Tires
#synthesize brand, size;
- (id)init {
if (self = [super init]) {
[self setBrand:[[NSString alloc] initWithString:#""]];
[self setSize:0];
}
return self;
}
- (void)dealloc {
[super dealloc];
[brand release];
}
#end
And I synthesize a setter and getter in my View Controller:
#import <UIKit/UIKit.h>
#import "Tires.h"
#interface testViewController : UIViewController {
Tires *frontLeft, *frontRight, *backleft, *backRight;
}
#property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;
#end
====================================
#import "testViewController.h"
#implementation testViewController
#synthesize frontLeft, frontRight, backleft, backRight;
- (void)viewDidLoad {
[super viewDidLoad];
[self setFrontLeft:[[Tires alloc] init]];
}
- (void)dealloc {
[super dealloc];
}
#end
It dies after [self setFrontLeft:[[Tires alloc] init]] comes back. It compiles just fine and when I run the debugger it actually gets all the way through the init method on Tires, but once it comes back it just dies and the view never appears. However if I change the viewDidLoad method to:
- (void)viewDidLoad {
[super viewDidLoad];
frontLeft = [[Tires alloc] init];
}
It works just fine. I could just ditch the setter and access the frontLeft variable directly, but I was under the impression I should use setters and getters as much as possible and logically it seems like the setFrontLeft method should work.
This brings up an additional question that my coworkers keep asking in these regards (we are all new to Objective-C); why use a setter and getter at all if you are in the same class as those setters and getters.
You have declared frontLeft as a 'copy' property:
#property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;
When you assign to this property, a copy is made by invoking the object's copy method. This only works for objects which support the NSCopying protocol (i.e., which implement a copyWithZone: method). Since your Tires class does not implement this method, you get an exception.
You probably want to change this to be a 'retain' property:
#property (nonatomic,retain) Tires *frontLeft, *frontRight, *backleft, *backRight;
See the Objective C documentation on declared properties for more on property declarations.
One problem that i see is here:
- (void)viewDidLoad {
[super viewDidLoad];
[self setFrontLeft:[[Tires alloc] init]];
}
When you call [Tires alloc] you get back an object with a retain count of 1. You then use a set method which you have synthesized, which bumps the retain count to 2. When your object is done with the Tire object, it will reduce the retain count back to 1, but the tire will never get deallocated. I think you should use:
[self setFrontLeft:[[[Tires alloc] init] autorelease]];