Method does not get run - objective-c

I am trying to run a non-void function returning an NSArray, but when I run it, there's not even a log line:
- (NSArray *) arrayFunction
{
return myList;
}
This is how I call the function:
- (void) myMainFunction
{
[self arrayFunction];
}
I also tried with NSLog and a void function instead of NSArray, but that won't show up either.
It is a NSView class.
Thanks for you help!
*EDIT: * Full Code:
Implementation file:
#import "LogNavigator.h"
#implementation LogNavigator
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
[self myMainFunction];
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
// Drawing code here.
}
- (NSArray *) arrayFunction
{
// # Get the list of .txt files, this part works correctly as expected in CodeRunner
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"Desktop"];
NSArray *directoryList = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil]
pathsMatchingExtensions:[NSArray arrayWithObjects:#"txt", nil]];
return directoryList;
}
- (void) myMainFunction
{
[self arrayFunction];
}
#end

If this custom NSView is created using Interface Builder then you should override awakeFromNib:
- (void)awakeFromNib {
[self myMainFunction];
}
Note, however, that you are ignoring the return from arrayFunction, so it's feasible the compiler might omit the call entirely during an optmimized release build if it can determine no side effects of the call.
EDIT: Note that you need to set the NSView-derived class within the view of the window within MainMenu.xib, using IB, in order for this method to be triggered when the view is loaded.

When objects are loaded from a nib (or a Storyboard) The normal initialiser is not called, instead initWithCoder is called.
When setting up objects from a nib, you should override initWithCoder and put your initialiser code in there instead.
If you need to access or set up other objects in the nib as part of initialisation, then awakeFromNib is a better method to override, because it is called after all the other nib objects have been loaded.
edit
When you added the view object to the nib, did you specify it's class as LogNavigator? This is something that people frequently forget to do.

Related

ARC assigning instance variable init or awakeFromNib

I am trying to initialise a variable in my init method, but it seems to get deallocated when the call chain reaches awakeFromNib. I've read several threads and far too many seem to share the same confusion - something that should be really simple and straightforward... :(.
- (id)init {
self = [super initWithNibName:#"MyViewController" bundle:[NSBundle mainBundle]];
if (self) {
self.customObject = [CustomObject currentInstance]; //OK
}
}
- (void)awakeFromNib {
[self.customObject someMethod]; //here self.customObject is nil?
//self.customObject = [CustomObject currentInstance]; // Why should I do this here?
}
Nothing fancy about the declarations, in my .m file there's. I don't want to assign the property in awakeFromNib without understanding the reason behind.
#property (strong) CustomObject *customObject;
CustomObject instantiation .m file
static CustomObject customObject;
#implementation CustomObject {
+ (instancetype)initMockCustomObject {
customObject = (CustomObject*)[OCMockObject mockForClass:[CustomObject class]];
}
+ (instancetype)currentInstance {
if ( customObject == nil) { [NSException raise...]; }
return customObject;
}
}
initMockCustomObject has already been called in AppDelegate.
Edit: added the actual super call to initWithNibName method in init.
Edit2: added the singleton instance
thank you for your input. I've created a sandbox project with the absolute minimum, and I found that I had an object in one of my parent NIBs, that was of the same type as MyViewController, causing this problem. Closing this question as it's too app specific. The fundamental question of initialising variables in init is OK.

Property not set in drawRect method - iOS

I have been seeing some strange behavior when I try to access a class variable or a property in my drawRect method..
In my .h file I have the following
#interface DartBoard : UIView
{
Board * board;
int index;
}
#property (readwrite, assign, nonatomic) NSNumber * selectedIndex;
#end
In my .m file I have the following
#implementation DartBoard
#synthesize selectedIndex;
-(id)init
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"selectedIndex: %d",[self.selectedIndex intValue]);
NSLog(#"index: %d",index);
}
#end
the output is
2012-06-12 19:48:42.579 App [3690:707] selectedIndex: 0
2012-06-12 19:48:42.580 App [3690:707] index: 0
I have been trying to find a solution but have had no luck..
I found a similar question but there was no real answer to the issue
See: UIView drawRect; class variables out of scope
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly but how do I fix it?
Cheers
Damien
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly
No, there is nothing special about -drawRect:.
There are two possibilities:
1. Your -init method is not being called.
You didn't say how this view gets created -- if you are manually calling [[DartBoard alloc] init], or if it is getting unarchived from a nib file.
If it's coming from a nib, UIView's unarchiving doesn't know that your init method should be called. It will call the designated initializer instead, which is -initWithFrame:.
So, you should implement that method instead, and make sure to call super!
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
}
return self;
}
2. There might be two instances of your view: one that you are manually initing, and another one that comes from somewhere else, probably a nib. The second instance is the one that is being drawn. Since its variables and properties are never set, they show up as zero (the default value).
You could add this line to both your -init and -drawRect: methods, to see what the value of self is. (Or, check it using the debugger.)
NSLog(#"self is %p", self);

Get all instances of a class in objective c?

I have a UIView that has many instances and each one of them has a UIRecognizer.
When on of them is tapped I want to remove all the recognizers of the others.
What I want it to get all the instances of the class and remove their recognizes.
I know ManagedObjects has [Entity allObjects];
How can I create my "all objects" class method ?
I have two ideas:
1/ Create a class array with all the instances static NSArray* instances;, register them when initializing, unregister when deallocating. The array should have only weak references, otherwise they will never be deallocated.
2/ NSNotification. All instances can wait for a notification and if you tap, you send the notification.
First I'd say whatever you're trying to accomplish can likely be better accomplished with NSNotificationCenter, that said, if you still need to do this, the following will work and will be ARC compliant.
In your .h add this:
+(NSArray *)allInstances;
Then, add this to the bottom of your class's .m:
-(id)init {
//Note, I suppose you may need to account for exotic init types if you are creating instances of your class in non-traditional ways. I see in the docs that initWithType: exists for example, not sure what storyboard calls
self = [super init];
[[self class] trackInstance:self];
return self;
}
-(void)dealloc {
[[self class] untrackInstance:self];
}
static NSMutableArray *allWeakInstances;
+(void)trackInstance:(id)instance {
if (!allWeakInstances) {
allWeakInstances = [NSMutableArray new];
}
NSValue *weakInstance = [NSValue valueWithNonretainedObject:instance];
[allWeakInstances addObject:weakInstance];
}
+(void)untrackInstance:(id)instance {
NSValue *weakInstance = [NSValue valueWithNonretainedObject:instance];
[allWeakInstances removeObject:weakInstance];
}
+(NSArray *)allInstances {
NSMutableArray *allInstances = [NSMutableArray new];
for (NSValue *weakInstance in allWeakInstances) {
[allInstances addObject:[weakInstance nonretainedObjectValue]];
}
return allInstances.copy;
}
Then when you need all instances of the class, just call [Class allInstances];
If you just need to find all instances for debugging purposes, you can use the Allocations instrument and change the Recorded Types to only your class. This will give you a dandy list of all your objects. You can then interact with them using lldb by using their address.
If they're all subviews of the same view, you could iterate over parentView.subviews and find them that way. Something like this:
for (UIView *v in parentView.subviews) {
if ([v isKindOfClass:[MyViewClass class]]) {
// remove recognizer here
}
}
Another, more efficient, option would be to have a flag in your view controller that you set when the first recognizer is triggered and use to short-circuit any future recognizer handler calls. Something like this:
#property (nonatomic) BOOL shouldRespondToEvent;
#synthesize shouldRespondToEvent=_shouldRespondToEvent;
- (void)viewDidLoad {
[super viewDidLoad];
self.shouldRespondToEvent = YES;
// other viewDidLoad stuff here
}
- (void)gestureHandler:(UIGestureRecognizer*)recognizer {
if (!self.shouldRespondToEvent)
return;
self.shouldRespondToEvent = NO;
// rest of handler code here
}

Is this valid code to create a NIB-instantiated singleton?

Assume that I instantiate an object of class MyGreatClass in my NIB (as usual by simply dragging an "Object" to the NIB and settings its class to MyGreatClass).
I want access to that instance anywhere in my codebase, without introducing coupling, i.e. without passing objects around like crazy, and without having an outlet to it in, say, [NSApp delegate]. (The latter would make AppDelegate terribly bulky with time.)
I ask: Is the following considered a good code to accomplish this?
//imports
static MyGreatClass *theInstance = nil;
#implementation MyGreatClass
+ (MyGreatClass *)sharedInstance
{
NSAssert(theInstance != nil, #"instance should have been loaded from NIB");
return theInstance;
}
- (id)init //waking up from NIB will call this
{
if (!theInstance)
theInstance = self;
return theInstance;
}
// ...
If this work as expected, I would after the app is loaded be able to access my instance via sharedInstance.
What do you think?
UPDATE: Hmm, on the second thought, the above init method maybe overkill. This is way simpler to think about:
- (id)init
{
NSAssert(!theInstance, #"instance shouldn't exist yet because only "
#"the NIB-awaking process should call this method");
theInstance = self;
return theInstance;
}
Again, what do you think?
The proper way to create a singleton is to override allocWithZone: to ensure another object cannot be created. Overriding init allows the new object to be created, but not initialized. It is thrown away because the init method simply ignores it and returns the object that has been created already. Here is how I would do it:
+ (MyGreatClass *)sharedInstance {
NSAssert(theInstance != nil, #"instance should have been created from NIB");
return theInstance;
}
+ (MyGreatClass *)allocWithZone:(NSZone *)zone {
if(theInstance) return theInstance;
return [[self alloc] init];
}
- (id)init {
if(theInstance) return theInstance;
if(self = [super init]) {
theInstance = self;
// other initialization
}
return self;
}
- (void)release {}
- (void)dealloc {
return;
[super dealloc]; // Prevent compiler from issuing warning for not calling super
}
I overrode release and dealloc to ensure that the singleton would not be deallocated. If you don't do this, you should retain and autorelease it in the sharedInstance method. If you want to support multithreading, you should also synchronize access to the theInstance variable.

Problem with NSObject init method

I have a problem with the init() method of a standard NSObject. I wrote a class (EFAPersistence) which is a subclass of NSObject. EFAPersistance has a attribute called efaDatabase.
EFAPersistence.h
#interface EFAPersistence : NSObject {
FMDatabase * efaDatabase;
}
#property (assign) FMDatabase * efaDatabase;
Everytime an instance of EFAPersistance is created I want to assign efaDatabase a value from my AppDelegate.
EFAPersistence.m
#implementation EFAPersistence
#synthesize efaDatabase;
- (id)init {
if (self = [super init]) {
efaDatabase = [[NSApp delegate] efaDatabase];
}
return self;
}
#end
This way of assigning does not work. But it works if I put the code in a normal method. So I am sure that efaDatabase is correctly instantiated in my AppDelegate. It's just not working in my init() method. That's why I have the feeling that NSApp is not working inside the init() method.
That's how the important AppDelegate code looks like.
AppDelegate.h
#interface AppDelegate : NSObject <NSApplicationDelegate> {
FMDatabase * efaDatabase;
}
AppDelegate.m
- (id)init {
if (self = [super init]) {
NSString * databasePath =
[[NSBundle mainBundle] pathForResource:#"efa" ofType:#"sqlite"];
self.efaDatabase = [FMDatabase databaseWithPath:databasePath];
if (![efaDatabase open]) {
NSLog(#"Couldn't open database: %#", databasePath);
// TODO: Create a database here
}
self.db = [[EFAPersistence alloc] init];
}
return self;
}
As you can see I am calling the init method. I also affirmed this by using NSLog(). init() is called. The attribute I am trying to assign in EFAPersistence is also created before init() is called.
To sum everything up:
How can I make this work within the init() method so I do not have to write boiler plate code in all my methods of EFAPersistence?
It looks to me that your AppDelegate is unset when you try to create the EFAPersistance object the first time. This is on below line in AppDelegate.m
self.db = [[EFAPersistence alloc] init];
I imagine the app delegate is set after the init is done (returned).
This way of assigning does not work. But it works if I put the code in a normal method. So I am sure that efaDatabase is correctly instantiated in my AppDelegate. It's just not working in my init() method. That's why I have the feeling that NSApp is not working inside the init() method.
NSApp works fine.
Quoting epatel:
I imagine the app delegate is set after the init is done (returned).
Correct. The nib loader completely instantiates each object (including the app delegate, if it's in a nib), then sets it as the value of any properties it's connected to. These are two separate operations; it will not set a not-yet-initialized object as the application delegate.
Quoting you (Jens) again:
The question is how to assign efaDatabase in EFAPersistences only once . There are other methods like awakeFromNib and viewDidLoad etc. But those are not available in a plain NSObject subclass.
Incorrect. awakeFromNib is sent to every object in a nib after the object has been initialized.
That said, I'm curious as to why you have EFAPersistence in a nib. From its name, it doesn't sound interface-related. Shouldn't the app delegate own the EFAPersistence, and the EFAPersistence own the database directly?