this is a follow-up question to my last one here: iOS: Initialise object at start of application for all controllers to use .
I have set my application up as follows (ignore the DB Prefix):
DBFactoryClass // Built a DataManaging Object for later use in the app
DBDataModel // Is created by the factory, holds all data & access methods
DBViewControllerA // Will show some of the data that DBDataModel holds
moreViewControllers that will need access to the same DBDataModel Object
i will go step by step through the application, and will then in the end post the error message i get when building.
AppDelegate.h
#import "DBFactoryClass.h"
AppDelegate.m
- (BOOL)...didFinishLaunching...
{
DBFactoryClass *FACTORY = [[DBFactoryClass alloc ]init ];
return YES;
}
DBFactoryClass.h
#import <Foundation/Foundation.h>
#import "DBDataModel.h"
#interface DBFactoryClass : NSObject
#property (strong) DBDataModel *DATAMODEL;
#end
DBFactoryClass.m
#import "DBFactoryClass.h"
#implementation DBFactoryClass
#synthesize DATAMODEL;
-(id)init{
self = [super init];
[self setDATAMODEL:[[DBDataModel alloc]init ]];
return self;
}
#end
ViewControllerA.h
#import <UIKit/UIKit.h>
#import "DBDataModel.h"
#class DBDataModel;
#interface todayViewController : UIViewController
#property (strong)DBDataModel *DATAMODEL;
#property (weak, nonatomic) IBOutlet UILabel *testLabel;
#end
ViewControllerA.m
#import "todayViewController.h"
#implementation todayViewController
#synthesize testLabel;
#synthesize DATAMODEL;
- (void)viewDidLoad
{
todaySpentLabel.text = [[DATAMODEL test]stringValue];
}
#end
DBDataModel.h
#import <Foundation/Foundation.h>
#interface DBDataModel : NSObject
#property (nonatomic, retain) NSNumber* test;
#end
DBDataModel.m
#import "DBDataModel.h"
#implementation DBDataModel
#synthesize test;
-(id)init{
test = [[NSNumber alloc]initWithInt:4];
return self;
}
#end
when i build it, i get the following error: EXC_BAD_ACCESS in this line:
#synthesize DATAMODEL;
of DBFactoryClass.m
What #synthesize does is to automatically generate implementations of the accessors for a property. EXC_BAD_ACCESS there means that you're accessing garbage when one of the accessors is executed.
That's probably happening here:
[self setDATAMODEL:[[DBDataModel alloc]init ]];
Make sure that DBDataModel's implementation of init actually returns a legitimate object.
As far as I can tell, your DBFactoryClass class is never stored anywhere, and therefore released right after the allocation if you use ARC (Since you use the strong keyword I assumed you do).
- (BOOL)...didFinishLaunching... {
DBFactoryClass *FACTORY = [[DBFactoryClass alloc ]init ];
// If you use ARC this might be released right afterwards
return YES;
}
If you want the factory to be a singleton, use something like this
+ (id)sharedInstance {
static dispatch_once_t once;
static MyFoo *instance;
dispatch_once(&once, ^{
instance = [[self alloc] init];
});
return instance;
}
Related
I want to provide methods used in several view controllers called in my delegate methods.
For example, I have some CloudKit functionality (I've added this to my own framework, but I don't think thats important), where I want to provide some crash logging.
Previosuly I had a crashLog function in each of my view controllers, which worked fine, but I have a lot of duplicate code.
Therefore I'd like to produce a category with these methods instead.
However I'm having difficulty getting my delegate methods to see these category methods.
Here's my code..
UIViewController+CloudKitDelegates.h
#interface UIViewController (CloudKitDelegates) <iCloudDBDelegate>
#property (weak,nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...;
#end
UIViewController+CloudKitDelegates.m
#import "UIViewController+CloudKitDelegates.h"
#implementation UIViewController (CloudKitDelegates)
#dynamic iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...
{
va_list args;
va_start(args, message);
NSLog(#"%#", [[NSString alloc] initWithFormat:message arguments:args]);
va_end(args);
}
#end
h file - my calling view controller (e.g. My View Controller)
#import "UIViewController+CloudKitDelegates.h"
m file - delegate method
-(NSString*)getDBPath
{
[self.iCloudDBDelegate crashLog: #"testing"];
From this call I'm getting an error ...
'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]:
unrecognized selector sent to instance
The error is showing that my calling view controller called MyViewController doesn't have the crashLog method, which I have in my category.
Any ideas where I'm going wrong ?
The problem: identical method crashLog: in multiple classes, for example
#interface ViewController : UIViewController
#end
#implementation ViewController
- (void)someMethod {
[self crashLog:#"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
Solution A: move crashLog: to a common superclass (or a category on superclass UIViewController)
#interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
#end
#implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface ViewController : CommonViewController
#end
#implementation ViewController
- (void)someMethod {
[self crashLog:#"error"];
}
#end
Solution B: move crashLog: to a delegate and protocol
#protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
#end
#interface DelegateClass : AnyClass <ICloudDBDelegate>
#end
#implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface ViewController : UIViewController
#end
#implementation ViewController
#property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:#"error"];
}
#end
#interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
#property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
#end
#implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
#end
Now we have new problem: property iCloudDBDelegate in multiple classes
Solution B + A: move crashLog to a delegate, move iCloudDBDelegate property to a superclass
#protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
#end
#interface DelegateClass : AnyClass <ICloudDBDelegate>
#end
#implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface CommonViewController : UIViewController
#property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
#end
#implementation CommonViewController
#end
#interface ViewController : CommonViewController
#end
#implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:#"error"];
}
#end
Solution C:
Another approach is a singleton object like NSUserDefaults.standardUserDefaults or NSFontManager.sharedFontManager: CloudDBManager.sharedCloudDBManager. No category or protocol required, just include CloudDBManager.h and use CloudDBManager.sharedCloudDBManager from everywhere.
#interface CloudDBManager : NSObject
#property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
#end
#implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(#"%#", message);
}
#end
#interface ViewController : CommonViewController
#end
#implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:#"error"];
}
#end
(I've added this to my own framework, but I don't think thats important)
Yep, that's the typical problem. You've failed to include -ObjC in the link flags.
See Building Objective-C static libraries with categories. This applies to frameworks as well.
ObjC does not create linker symbols for methods. It can't, they're not resolved until runtime. So the category methods aren't seen by the linker as "missing" and it doesn't bother linking the relevant compile unit. This is an important optimization that keeps you from linking all of a massive C library just because you use one function in it, but Objective-C categories break some of the linker's assumptions. The compiler saw the definition (via the header), but the linker didn't care, so there's no error until runtime.
The -ObjC flag says "this C-looking compile unit is actually Objective-C; link all of it even if you don't think you need to."
Is it possible to extend an derived class from NSManagedObject? I'm asking this because I tried to do it. My entity looks like this:
So this means a class similar to the following code should be generated:
#import <Foundation/Foundation.h>
#interface Player : NSManagedObject
#property (nonatomic, copy) NSNumber* orderNumber;
#property (nonatomic, copy) NSString *name;
#end
.m file
#import "Player.h"
#implementation Player
#dynamic name, orderNumber;
#end
This two variables are saved to the SQLite database.
Now since I need some additional variables for the player I just added them to the class. It still worked.
#import "Player.h"
#implementation Player
#dynamic name, orderNumber;
- (id) init
{
self = [super init];
if (self != nil)
{
[self reset];
}
return self;
}
#synthesize isStillInGame = _isStillInGame;
- (void) reset
{
_isStillInGame = TRUE;
}
- (void) setOutOfGame
{
_isStillInGame = FALSE;
}
#end
But now when I change the isStillInGame bool, all instances of the Player Class are changed. Is this normal or do I have an error in my code?
A second question I can't answer is, why I can't access the object variables while debugging. When I try to watch an Player instance variable I just see this:
Is it possible to see more?
I'm new to xCode. I'm using xCode 4.6 and I don't understand how xcode instantiates objects fully.
I thought that if you declare the object as a property in the .h file it automatically alloc and init it. the only way I could get my code to work is to do the alloc and init on the property file. I included my sample code below, but can anyone tell me if this is the right way to do this?
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic) int age;
#end
#import "Person.h"
#implementation Person
#end
#import <UIKit/UIKit.h>
#import "Person.h"
#interface ViewController : UIViewController
#property (strong, nonatomic) Person *person;
#property (weak, nonatomic) IBOutlet UILabel *lblDisplay;
- (IBAction)btnChangeLabel:(id)sender;
#end
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_person = [[Person alloc]init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnChangeLabel:(id)sender {
[_person setName:#"Rich"];
[_person setAge:50];
_lblDisplay.text = [NSString stringWithFormat:#"%# is %d years old.",_person.name,_person.age];
}
#end
You're doing the right thing, except in your btnChangeLabel method, refer to your properties via:
self.person.name = #"Rich";
self.person.age = 50;
_lblDisplay.text = [NSString stringWithFormat:#"%# is %d years old.",self.person.name,self.person.age];
The only time you want to use the underlying variables "_person" is when you need to allocate the space for them. The rest of the time, you'll use their accessors (getters and setters; which is what the "self.person.name =" thing is doing). This way the compiler will know to do ARC-style releases and retains automagically.
I recently started learning Objective-C and Cocos-2D. I tried to define my own method for automating the creation of sprites.
I added my own class where I'll create other automation methods as well. Anyhow my .h file looks like this:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface ActionsClass : CCNode {
}
#property (nonatomic, strong) CCSprite* createSprite;
#property (nonatomic, strong) CCSprite* spriteName;
#property (nonatomic, strong) NSString* pngName;
#property (nonatomic) CGPoint* spriteCoordinate;
- (CCSprite *)createSprite: (CCSprite *)spriteName: (NSString *)pngName: (CGPoint *)spriteCoordinate;
#end
And the .m is:
#import "ActionsClass.h"
#implementation ActionsClass
#synthesize createSprite = _createSprite;
#synthesize spriteName = _spriteName;
#synthesize pngName = _pngName;
#synthesize spriteCoordinate = _spriteCoordinate;
- (CCSprite *)createSprite: (CCSprite *)spriteName: (NSString *)pngName: (CGPoint *)spriteCoordinate
{
if (!_createSprite)
{
_createSprite = [[CCSprite alloc] init];
_spriteName = [CCSprite spriteWithFile:_pngName];
_spriteName.position = ccp(_spriteCoordinate->x, _spriteCoordinate->y);
[self addChild:_spriteName];
}
return _createSprite;
}
#end
In the main .m file where I want to call the method:
[self createSprite: saif: #"saif.png": ccp(100,100)];
This would give the warning that xcode didn't find the instance method createSprite and defaults it to id
Thanks a lot and sorry if the font or the formatting of the question aren't super neat.
Your method declaration is wrong, so you wont be able to call it.
It should be:
- (CCSprite *)createSprite:(CCSprite *)spriteName pngName:(NSString *)pngName coord:(CGPoint *)spriteCoordinate;
And called like:
[self createSprite:someSprite pngName:somePNGName coord:someCoord];
Edit: I didn't see that you were trying to call this from another class. To do that you will need to import the ActionsClass header file, and call this method on an instance of ActionsClass, e.g.
ActionsClass *actionsClassObject = [[ActionsClass alloc] init];
[actionsClassObject createSprite:someSprite pngName:somePNGName coord:someCoord];
I'm new to ObjectiveC and Xcode and I'm having a strange behavior:
This is the Packet class header: Packet.h
#import <Foundation/Foundation.h>
#interface Packet : NSObject
{
short index;
NSData *packetData;
short totalChunks;
}
#property (nonatomic) short index;
#property (strong, nonatomic) NSData *packetData;
#property (nonatomic) short totalChunks;
#end
And this is Packet.m
#import "Packet.h"
#implementation Packet
#synthesize index;
#synthesize packetData;
#synthesize totalChunks;
-(id)init
{
self = [super init];
return self;
}
#end
The problem is i can't use getter and setter although it seems to me that properties are declared and synthesized correctly!
for example:
#import "TransferManager.h"
#implementation TransferManager
-(void)sendAckPacket
{
Packet *packet = [[Packet alloc] init];
packet.index; //ERROR HERE: Property not found on object of type Packet *
}
#end
I know there is no TransferManager.h attached becaues I don't want to write useless stuff, but it imports "Packet.h". Otherwise i should have error during the initialization of *packet.
I'm stuck... Any Help? :)