So long story short, I have some odd bugs in some code I'm messing with and it turns out that it comes from an object being identified as the wrong class.
Here is my code, with the NSLog statement I put before anything touches the object... I can't figure out why it's showing as the wrong class.
Photo.h
#interface Photo : NSObject
{
}
#property (nonatomic, retain) UIImage *image;
#property (nonatomic, retain) IBOutlet NSString *descr;
#end
Photo.m
#implementation Photo
#synthesize image;
#synthesize descr;
#end
CameraViewController.h
#include "Photo.h"
#interface CameraViewController : UIViewController <UIImagePickerControllerDelegate>
{
IBOutlet UIImageView *imageView;
}
#property (nonatomic, retain) Photo *photo;
-(void)takePhoto;
-(void)resetImage;
#end
and finally...
CameraViewController.m
#import "Photo.h"
....
#implementation CameraViewController
#synthesize photo;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", NSStringFromClass([_photo class]));
// Do any additional setup after loading the view.
}
This prints out:
UIImageView
I would expect it to print out
Photo
or at the very least
NSObject
Am I missing something???
Sending an object the class message retrieves that object's dynamic type. The object must be assigned prior to querying it for its class. In other words, declaring a property as Photo* is not sufficient for its class to return Photo: you must also assign it an instance of Photo or one of its subclasses. In the later case, the result of sending class message will be different (i.e. the Class of the subclass will be returned).
Related
I am trying to make a TableViewController.. I got it to work using code from a youtube lesson: "Cocoa Programming L13-14" But then when I try to change it so that the default values aren't hard coded... but rather the values of controls in the Interface Builder, I get (null) across the board. Here is the code:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
IBOutlet NSPathControl* pcSource;
IBOutlet NSPathControl* pcDestination;
IBOutlet NSTextField* tfBackupAmount;
NSURL* urlSource;
NSURL* urlDestination;
NSString* strBackupAmount;
//Old--
//NSString* name;
//int age;
}
#property NSURL* urlSource;
#property NSURL* urlDestination;
#property NSString* strBackupAmount;
//Old--
//#property (copy) NSString* name;
//#property int age;
#end
and
#import "Person.h"
#implementation Person
#synthesize urlSource;
#synthesize urlDestination;
#synthesize strBackupAmount;
//Old--
//#synthesize name;
//#synthesize age;
- (id)init {
self = [super init];
if (self) {
urlSource = [pcSource URL];
urlDestination = [pcDestination URL];
strBackupAmount = [tfBackupAmount stringValue];
NSLog(#"%#\n%#\n%#",urlSource,urlDestination,strBackupAmount);
//Old--
//name = #"Yoda";
//age = 900;
//NSLog(#"%#: %i", name, age);
}
return self;
}
#end
Everything commented //Old-- worked, and interacted fine with the TableViewController. So I am assuming all that still works fine. The 3 controls (2 NSPathControl & 1 NSTextField) are linked up to an Object class:Person in Interface Builder with the controls linked up. Why am I getting output of:
(null)
(null)
(null)
? When I get to the NSLog(); line? Where am I going wrong?
Thanks!
pcSource, pcDestination, or tfBackupAmount aren't initialized when your init method is called, so they're all nil. Sending a message to nil is legal in Objective-C, and you'll just get nil back. That means urlSource, urlDestination, and strBackupAmount are all nil too, and that's why you ge the log output you're seeing.
You need to change the log message to sometime after those variables are initialized.
Try putting the code in -viewDidLoad rather than -init. It all has to do with the order of events (-init gets called before any of the IB stuff happens.
Ok, technically this question - I found an answer for it. It is to make a custom init method. In my case this means:
Person* p = [[Person alloc] initWithurlSource:[NSURL URLWithString:#"moo"] andurlDestination:[NSURL URLWithString:#"cow"] andstrBackupAmount:#"foo"];
However this still doesn't solve my problem of getting values for IBOutlets from another Class (in this case my TableViewController class) that has been exposed as #property:
#interface AppDelegate : NSObject <NSApplicationDelegate> {
.....
.....
#property (nonatomic, retain) NSPathControl* pcSource;
#property (nonatomic, retain) NSPathControl* pcDestination;
#property (nonatomic, retain) NSTextField* tfBackupAmount;
I am still having trouble getting values for these controls in my "addButtonPressed" method with:
//ad is AppDelegate - declared in interface as AppDelegate* ad;
NSPathControl* pcSource = [ad pcSource];
NSPathControl* pcDestination = [ad pcDestination];
NSTextField* tfBackupAmount = [ad tfBackupAmount];
Here is a case where I am passing some parameters to a method, and then assigning the parameter values to local ivars and properties:
- (void) assignOwnerView:(UIView*)oView andPosition:(menuPosition)position withTopView:(UIView*)topView {
self.topView = topView;
self.ownerView = oView;
self.position = position;
<< --- other code --- >>
}
The interface for these properties is this (UPDATED with SYNTHESIS)
#interface MenuVC (){
UIView *ownerView_;
UIView *topView_;
menuPosition position_;
}
#property (nonatomic, retain) UIView *ownerView;
#property (nonatomic, retain) UIView *topView;
#property (assign) menuPosition position;
#end
#implementation MenuVC
#synthesize list, menuDelegate;
#synthesize ownerView = ownerView_;
#synthesize topView = topView_;
#synthesize position = position_;
- (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
{...
The enum is defined here:
typedef enum {
above,
below,
centered
} menuPosition;
After executing the three assignments, at a breakpoint in the debugger, the values are as shown below:
The received parameter values look okay, but the assigned values of the ivars ownerView_ and position_ are not right. On the other hand, topView is okay.
The same thing happened when I assigned directly to ivars rather than properties.
I started seeing this problem when I upgraded to Lion (10.7.3) and XCode 4.3.1. It was working okay before then. I see this at other spots in my app, and I don't see any pattern to it yet.
ARC is not being used.
I reported this problem before, but didn't get an answer. In this case, the problem description is a simpler. That might make it easier to see what the problem is.
UPDATE -- header file added
#import <UIKit/UIKit.h>
#class MenuVC;
#protocol menuDelegateProtocol
typedef enum {
above,
below,
centered
} menuPosition;
- (void) didSelectItemFromMenu:(MenuVC *)menu atIndex:(NSUInteger) index;
#end
#interface MenuVC : UITableViewController {
NSArray *list;
float extendedHeight;
id<menuDelegateProtocol> menuDelegate;
}
#property (nonatomic, retain) NSArray *list;
#property (nonatomic, assign) id<menuDelegateProtocol> menuDelegate;
- (id) initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
- (void) hide;
- (void) unhide;
- (void) assignOwnerView:(UIView*)oView andPosition:(menuPosition)position withTopView:(UIView*)topView;
#end
You didn't synthesized the properties.
Put:
#synthetize ownerView = ownerView_;
#synthetize topView = topView_;
#synthetize position = position_;
After "#implementation MenuVC".
I changed the debugger to GDB from LLDB. This seems to be a problem with LLDB. I'm not seeing this now.
I'm trying to learn how to set variables for different classes using one main data class.
Here's a diagram of of what I would like to do and the code from my project:
ClassA
#import <Foundation/Foundation.h>
#interface ClassA : NSObject {
NSString *stringA;
NSString *stringB;
}
#property (nonatomic, copy) NSString *stringA;
#property (nonatomic, copy) NSString *stringB;
#property (weak) IBOutlet NSTextField *textA;
#property (weak) IBOutlet NSTextField *textB;
- (IBAction)displayStrings:(id)sender;
#end
#import "ClassA.h"
#implementation ClassA
#synthesize stringA, stringB, textA, textB;
- (IBAction)displayStrings:(id)sender {
[textA setStringValue:stringA];
[textB setStringValue:stringB];
}
#end
Class X
#import <Foundation/Foundation.h>
#interface ClassX : NSObject {
NSMutableString *stringX;
}
- (void)theVariables:(id)sender;
#end
#import "ClassX.h"
#import "ClassA.h"
#implementation ClassX
- (void)awakeFromNib {
[self theVariables:self];
}
- (void)theVariables:(id)sender {
stringX = [[NSMutableString alloc] init];
ClassA *clssA = [[ClassA alloc] init];
[stringX setString:#"stringX for stringA"];
[clssA setStringA:stringX];
[stringX setString:#"stringX for stringB"];
[clssA setStringB:stringX];
}
#end
No errors show up in the code, but when I run the program I am receiving an error about "Invalid parameter not satisfying: aString". It looks like the setStringValue for the IBOutlet is not working. Any suggestions?
I'm not seeing the error you mention, but as far as I can tell from your code, the main problem is this line:
ClassA *clssA = [[ClassA alloc] init];
You must have an instance of ClassA in your xib, which is connected to text fields and a button. That object in the xib is a real object, and if you just create another instance of ClassA somewhere in your code, you have an entirely different object that has no connection to the one that's in your xib.
You need to make sure of/change two things. First, there needs to be an instance of ClassX in your xib. Second, ClassX needs an outlet to a ClassA instance:
#class ClassA; // Declare ClassA so you can use it below
#interface ClassX : NSObject
#property (weak) IBOutlet ClassA * theClassAInstance;
- (void)theVariables:(id)sender;
#end
Which should then be connected in the xib file. Then, in theVariables:, you just use that outlet instead of creating a new instance of ClassA: [[self theClassAInstance] setStringA:#"stringX for stringA"];
Three points of style:
First, you should be importing Cocoa.h: #import <Cocoa/Cocoa.h> instead of Foundation.h in any class that touches the GUI (ClassA in this case). That's where stuff like NSTextField is defined. It works anyways because Cocoa.h is imported via your .pch file, but it's best to be explicit.
Second, creating a mutable string and changing its value to two different literal strings doesn't make a whole lot of sense. Just use the literals directly: [clssA setStringA:#"stringX for stringA"];
Third, You don't need to declare the instance variables separately; #synthesize creates them for you, and it is now the recommended practice to not declare them:
#interface ClassA : NSObject
#property (nonatomic, copy) NSString *stringA;
#property (nonatomic, copy) NSString *stringB;
#property (weak) IBOutlet NSTextField *textA;
#property (weak) IBOutlet NSTextField *textB;
- (IBAction)displayStrings:(id)sender;
#end
Last (four points!), you should really be accessing the values of stringA and stringB in ClassA via the property: [textA setStringValue:[self stringA]];
I have a hierarchy of model objects which I will be displaying on different type of UITableViewCell subclasses. All decision is made on the fly as to which model object should be used and corresponding UITableViewCell subclass' object is spawned and then set the model object to the UITableViewCell's subclass object so that it can fetch values from it.
My UITableViewCell hierarchy is something like this:
The base class Cell hierarchy:
#interface BaseCell : UITableViewCell
{
Base *baseObj_;
}
#end
The subclass of cell hierarchy:
#interface DerivedCell : BaseCell
{
}
#property (nonatomic, retain) Derived *derivedObject;
#end
#implementation DerivedCell
#synthesize derivedObject = baseObj_;
#end
The base class of Model object:
#interface Base : NSObject
{
NSString *title_;
}
#property (nonatomic, retain) NSString *title;
#end
The subclass of model hierarchy
#interface Derived : Base
{
NSString *detailedText_;
}
#property (nonatomic, retain) NSString *detailedText;
#end
When I do so, I am having errors in this line:
#synthesize derivedObject = baseObj_;
Which reads:
Property 'derivedObject' attempting to use ivar 'baseObj_' declared in super class BaseCell.
Type of property 'derivedObject' (Derived*) does not match type of ivar 'baseObj_' ('Base * __strong')
I want to use properties and synthesize them so that I can leverage the uses of properties (like using dot notation etc.). I have for now used accessors and setters which solves the problem:
#interface DerivedCell : BaseCell
{
}
-(Derived*)derivedObject;
-(void)setDerivedObject:(Derived*)newDerivedObject;
#end
But I was just wondering if I could somehow fix these errors to use the properties only.
Thanks,
Raj
Try the below code I have modified your code a bit as shown below
Since you can assign class Base object to class Derived in #synthesize, it can be achieved by this way, I know you have tried it already, I have tried it with the below code and able to access the variables with dot, try the below code and let me know if it is not working
#interface DerivedCell : BaseCell
{
Derived *derivedObject;
}
#property (nonatomic, retain) Derived *derivedObject;
#end
#implementation DerivedCell
#dynamic derivedObject;
- (void)setDerivedObject:(Base *)baseObj {
if (self.derivedObject == nil) {
derivedObject = [[Derived alloc] init];
}
derivedObject.detailedText = baseObj.title;
}
- (Derived *)derivedObject {
return derivedObject;
}
#interface Derived : Base
{
NSString *detailedText_;
}
#property (nonatomic, retain) NSString *detailedText;
#end
#implementation Derived
#synthesize detailedText = detailedText_;
#end
#interface BaseCell : UITableViewCell
{
Base *baseObj_;
}
#property (nonatomic, retain) Base *baseObj;
#end
#implementation BaseCell
#synthesize baseObj = baseObj_;
#end
#interface Base : NSObject
{
NSString *title_;
}
#property (nonatomic, retain) NSString *title;
#end
#implementation Base
#synthesize title = title_;
#end
Base *b = [[Base alloc] init];
b.title = #"Hello Raj";
BaseCell *bc = [[BaseCell alloc] init];
bc.baseObj = b;
DerivedCell *dc = [[DerivedCell alloc] init];
dc.derivedObject = b;
NSLog(#"Derive dc %#", dc.derivedObject.detailedText);
Another Solution which I have provided has an issue when I checked it
#interface BaseCell : UITableViewCell
{
NSString *baseTitle_;
}
#property (nonatomic, retain) NSString *baseTitle;
#end
#implementation BaseCell
#synthesize baseTitle = baseTitle_;
#end
#interface DerivedCell : BaseCell
{
NSString *derivedTitle_;
}
#property (nonatomic, retain) NSString *derivedTitle;
#implementation DerivedCell
#synthesize derivedTitle = baseTitle;
#end
When I created instance for the class and as shown below
DerivedCell *dCell = [[DerivedCell alloc] init];
dCell.baseTitle = #"Hello";
NSLog(#"%#",dCell.baseTitle);//Output was Hello
NSLog(#"%#",dCell.derivedTitle);//Output was (null)
It didn't assign the value to derivedTitle, If it is working for you please let me know
Another solution with memory referncing
#interface BaseCell : UITableViewCell
{
NSMutableString *baseTitle_;
}
#property (nonatomic, retain) NSMutableString *baseTitle;
#end
#implementation BaseCell
#synthesize baseTitle = baseTitle_;
#end
#interface DerivedCell : BaseCell
{
}
#property (nonatomic, retain) NSMutableString *derivedTitle;
#end
#implementation DerivedCell
#synthesize derivedTitle;
- (id) init
{
if ( self = [super init] )
{
baseTitle_ = [[NSMutableString alloc] init];
derivedTitle = baseTitle_;
}
return self;
}
#end
DerivedCell *dCell = [[DerivedCell alloc] init];
[dCell.baseTitle appendString:#"Hello"];
NSLog(#"baseTitle : %#",dCell.baseTitle);
NSLog(#"derivedTitle :%#",dCell.derivedTitle);
Console Output baseTitle : Hello derivedTitle :Hello
One pattern I've used for situations like this is to re-declare the property in a category on the derived class. The one structural change this approach requires from the code you posted is that it requires a same-named property (or equivalent getter/setter methods) to be defined in the base class. Consider the following snippet:
#interface BaseModel : NSObject
#end
#interface DerivedModel : BaseModel
#end
#interface BaseCell : UITableViewCell
{
BaseModel *baseObj_;
}
#property (nonatomic, retain) BaseModel *modelObject;
#end
#interface DerivedCell : BaseCell
#end
#interface DerivedCell (DowntypedPropertyCategory)
#property (nonatomic, retain) DerivedModel *modelObject;
#end
#implementation BaseModel
#end
#implementation DerivedModel
#end
#implementation BaseCell
#synthesize modelObject = baseObj_;
#end
#implementation DerivedCell
#end
In this pattern, the base class declares the iVar and the base-typed property, and synthesizes the implementation. The derived class declares the downcast-typed property in a category. Being in a category, the compiler won't force you to implement methods for that property. This gets you out of trying to synthesize against a superclass's iVar, instead relying on implementations that exist in the superclass, but declaring them to be of a different type. At runtime, the runtime will just end up calling the superclass methods (since Obj-C method dispatch is based on selector only, and does not have multiple dispatch.) As a result, clients of these properties can do stuff like this without any compile time warnings or errors:
#interface UnrelatedObject : NSObject
#end
#implementation UnrelatedObject
- (void)unrelatedMethod: (DerivedCell*)dc
{
DerivedModel* dm = dc.modelObject;
NSLog(#"dm: %#", dm);
}
#end
Again, the catch/minor difference is that in order for this to work, the base class must define a property of the same name (or equivalent getter/setter methods). That said, the property/methods in the base class could be declared (or in the case of methods, NOT even delayed) and defined in the base class's implementation file only, and thus would not be visible to other files merely including the header.
One other note: by using this approach you're missing out on compile time checks for things like mismatch between the property specifiers ([nonatomic|atomic], [readonly|readwrite], [assign|retain|copy]). I've found this pattern incredibly useful, but there are some potential pitfalls to keep an eye out for.
I hope I understand the question correctly, how about typing the model as id?
#interface BaseCell : UITableViewCell
#property(retain, nonatomic) id model;
#end
#implementation BaseCell
#synthesize model;
#end
Then the derived cells can use whatever model classes they want.
When you initialize an instance variable through synthesize, that variable is not accesible from any class that may inherit it.
It looks like you may have been trying to point synthesize to a public instance variable and I'm not sure if that is possible. It may be trying to declare a new variable with the same name which I'm sure would generate some compiler warnings at the least since that new declaration would hide an existing one and is less accessible.
You could simply write your own getter and setter to expose the instance variable.
- (Base *) baseObj {
return _baseObj;
}
- (void) setBaseObj:(Base *)val {
if( val != _baseObj ) {
[_baseObj release];
_baseObj = [val retain];
}
}
Hope this helps!
I rarely ask questions, but this one is frustrating me as I can not find an answer anywhere!
I just need to call the function in GameChallenges.m and return the value to the view controller. GameChallenges.m will be called by different view controllers, thats why its separate. Please help!
I have a separate class file called GameChallenges.
This has a function/method in it:
in the .h
#class StatsViewController;
#interface GameChallenges : NSObject {
StatsViewController* statsController;
NSString* challengeTitle;
}
#property (nonatomic, retain) IBOutlet StatsViewController* statsController;
#property (assign) NSString* challengeTitle;
-(NSString*)checkChallenge:(int)challegeID;
#end
in the .m
#import "GameChallenges.h"
#import "StatsViewController.h"
#implementation GameChallenges
#synthesize challengeTitle,statsController;
-(NSString*)checkChallenge:(int)challegeID{
if(challegeID==1){
self.challengeTitle = #"Some Text.";
return challengeTitle;
}else if(challegeID==2){
self.challengeTitle = #"Some Other Text.";
return challengeTitle;
}
}
From a view controller called StatsViewController I am calling this method
in the .h
#class GameChallenges;
#interface StatsViewController : UIViewController {
UILabel* challengeIDDescText;
}
#property (nonatomic, retain) IBOutlet UILabel* challengeIDDescText;
#property (nonatomic, retain) IBOutlet GameChallenges* challenges;
#end
in the .m
[challenges checkChallenge:tempString];
challengeIDDescText.text = challenges.challengeTitle;
Your code is quite weird, I'd say this is what's happening:
-[GameChallenges checkChallenge] seems to expect an int and you call it with a variable called tempString that I guess is an NSString *. More likely than not, your method is ending without assigning challengeTitle and without a valid return value. Fix it with return nil as last statement and passing an int.
This kind of problems are very easy to solve using the debugger.
Also, have a look to Apple samples.
I'm beginner in objective-C, however, code in the GameChallenges.m looks weird to me... Wouldn't this be better?
challengeIDDescText.text = [challenges checkChallenge:challengeId];