Having trouble with class extension in Objective C, mac OS. Getting error 'NSInvalidArgumentException', no visible #interface declares the selector - objective-c

I'm working on exercise 3 on page 76 in Apple's developer pdf in the class categories and extensions section, "Programming with Objective C", found here: (https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html)
my XYZPerson header looks like this:
#import <Foundation/Foundation.h>
#interface XYZPerson : NSObject
#property (readonly) NSNumber *height;
#property (readonly) NSNumber *weight;
-(NSNumber *)measureHeight;
-(NSNumber *)measureWeight;
#end
My implementation file looks like this:
#import "XYZPerson.h"
#property (readwrite) NSNumber *height;
#property (readwrite) NSNumber *weight;
#end
/////////////////////
#implementation XYZPerson
-(NSNumber *)measureHeight{
if(!_height){
_height = [[NSNumber alloc] init];
}
return _height;
}
-(NSNumber *)measureWeight{
if(!_weight){
_weight = [[NSNumber alloc] init];
}
return _weight;
}
#end
And then in my main file I have:
#import <Foundation/Foundation.h>
#import "XYZPerson.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
XYZPerson *aPerson = [[XYZPerson alloc] init];
[aPerson setHeight: [NSNumber numberWithInt:72] ];
[aPerson setWeight: [NSNumber numberWithInt:190] ];
NSLog(#"%# %#",[aPerson measureHeight],[aPerson measureWeight]);
}
return 0;
}
There might be more than one error unrelated to the issue I brought up, I'm a huge novice at Objective C right now. The exact compiler error I am getting is on the line that says,
[aPerson setHeight: [NSNumber numberWithInt:72] ];
The compiler error reads, "ARC Semantic Issue. No visible #interface for 'XYZperson' declares the selector 'setWeight:'.

Oh doh.
You will not be able to call that method from OUTSIDE of your class implementation.
Overriding properties in class extensions in the .m file doesn't expose that to the world.
At least not to the compiler.
That's the whole point.
It's for when you want properties that are readonly to the world outside of that object, but internally to the object you still want to be able to conveniently do things.

You declared the property read-only in the header file, so to everything outside of the class, the properly is read-only, and thus a setHeight: method doesn't exist.

You have made he properties height and weight read only. Then in the main function you are using setWeight and setHeight.
You can't do that as that would be writing the weights but you explicitly set them to read only.

Place your properties in .m with class extension:
#interface XYZPerson ()
#property (readwrite) NSNumber *height;
#property (readwrite) NSNumber *weight;
#end
See Apple docs about class extension:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html

Related

Objective C Global Variable

Can anyone tell me where I'm going wrong here please.
I have created an NSobject called BeaconData. The header file is:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreBluetooth/CoreBluetooth.h>
#interface BeaconData : NSObject
#property (nonatomic, strong) NSMutableArray * jsonArray;
#property (nonatomic, retain) NSString * bMajor;
#property (nonatomic, retain) NSString * bMinor;
#property (nonatomic, retain) NSString * bUUID;
-(void) getData;
#end
The implementation file is then:
#import "BeaconData.h"
#define getDataURL #"http://www.eventav.biz/websitebeacons/library/json/files/beacons.txt"
#implementation BeaconData
#synthesize jsonArray, bUUID, bMajor, bMinor;
//Retrieve data
-(void) getData
{
extern NSString * bUUID;
extern NSString * bMajor;
extern NSString * bMinor;
NSURL * url = [NSURL URLWithString:getDataURL];
NSData * data = [NSData dataWithContentsOfURL:url];
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//Loop through Json Array
for (int i = 0; i < jsonArray.count; i++)
{
bUUID = [[jsonArray objectAtIndex:i] objectForKey:#"i_uuid"];
bMajor = [[jsonArray objectAtIndex:i] objectForKey:#"i_major"];
bMinor = [[jsonArray objectAtIndex:i] objectForKey:#"i_minor"];
}
}
#end
Next I try to call the Global variable bMajor in the main viewController.m file and print it out - just to see if it works, like this:
- (void)viewDidLoad {
[super viewDidLoad];
extern NSString * bMajor;
NSInteger beaconMajorInt = [bMajor integerValue];
NSLog (#"Beacon bMajor is %li", (long)beaconMajorInt);
But all I get is the following error:
Undefined symbols for architecture x86_64:
"_bMajor", referenced from:
-[ViewController viewDidLoad] in ViewController.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
You have declared your bMajor variable as a class property. This means that you must instantiate an instance of your BeaconData class to access the variable unless you include a class method.
However, in your code I see that you also want to make these variables global. It is redundant to declare a variable as a class property and then try to make it global. In objective-c simply declaring a variable outside the implementation section will make it global for all modules that import the file with the declaration. You do it like this:
NSString *bMajor = #"Your String";
#implementation BeaconData
// YOUR CLASS CODE
#end
You're using the extern keyword incorrectly. It should be used in .h class files to let anything that imports it know that they have access to this variable. You also must declare it like I showed in the .m class file
.h looks like this:
extern NSString *bMajor;
#interface BeaconData : NSObject
#end
Just because you can do this doesn't mean you should. Based on your code I would suspect what you want to do is turn your -getData instance method into a class method for a singleton that allows the class to manage these "global" variables while keeping with good coding practice.
This SO Q/A should provide you exactly what you need to create your singleton. I recommend you do it this way.
Then in your viewController you would access these variables by getting an instance of your class using the class method.

NSObject returns nothing XCode 6

I moved my app to XCode 6 and found this problem. I have NSObject and it stopped returning objects when initialized, I use XCode 6 iPhone 6 Simulator.
My .h file:
#import <Foundation/Foundation.h>
#interface RBGAlpha : NSObject{
NSString *red;
NSString *blue;
NSString *green;
NSString *alpha;
}
#property (nonatomic, retain) NSString *red;
#property (nonatomic, retain) NSString *blue;
#property (nonatomic, retain) NSString *green;
#property (nonatomic, retain) NSString *alpha;
-(id)initWithName:(NSString *)r bl:(NSString *)b gr:(NSString *)g al:(NSString *)a;
#end
my .m file
#import "RBGAlpha.h"
#implementation RBGAlpha
#synthesize red,blue,green,alpha;
-(id)initWithName:(NSString *)r bl:(NSString *)b gr:(NSString *)g al:(NSString *)a{
self = [super init];
if (self) {
self.red = r;
self.blue = b;
self.green = g;
self.alpha = a;
}
return self;
}
#end
I use something like this in viewDidLoad method to create my objects:
RBGAlpha *tmpObj=[[RBGAlpha alloc] initWithName:#"0.01" bl:#"0.01" gr:#"0.01" al:#"1.00"];
However, while running the app in Simulator iPhone 6 this returns nothing
Has anybody dealt with that kind of problem?
I think that you're being mislead. There is indeed a value, that is what 0x786... in the value field means.
Summary saying 0 objects is confusing. I cannot understand why it would say that, but I bet if you typed po tmpObj into LLDB it would not return nil but the address showing next to "Value".
If you want to see something more interesting from the Xcode debugger consider implementing debugQuickLookObject.
On a side note, you can omit the definition of your instances variables in
#interface RBGAlpha : NSObject{
NSString *red;
NSString *blue;
NSString *green;
NSString *alpha;
}
And you also don't need to #synthesize each of them anymore, the compiler included with Xcode 5 and up does this for you.

Objective-C error "No visible #interface for 'XYZPerson' declares the selector 'saySomething'

I am really new to Objective-C and when I was practicing the book exercises, I really am stuck here. Please help me solve this and I have been thinking what could cause this error for more than three hours. Still I didn't get it!
Best regards,
Raj.
Thanks in advance !
main.m
#import <Foundation/Foundation.h>
#import "XYZPerson.h"
#import "XYZShout.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
//XYZPerson *some = [[XYZPerson alloc]init];
XYZShout *some = [[XYZShout alloc]init];
[some sayHello];
// insert code here...
// NSLog(#"Hello, World!");
}
return 0;
}
XYZPerson.h
#import <Foundation/Foundation.h>
#interface XYZPerson : NSObject
#property NSString *firstName;
#property NSString *secondName;
#property NSDate *dob;
-(void) saySomething;
-(void) sayHello;
#end
XYZPerson.m
#import "XYZPerson.h"
#implementation XYZPerson
-(void) sayHello {
[self saySomething:#"Hello all"];
}
-(void) saySomething:(NSString *)greet {
NSLog(#"%#", greet);
}
#end
XYZShout.h
#import "XYZPerson.h"
#interface XYZShout : XYZPerson
// -(void) saySomething;
#end
XYZShout.m
#import "XYZShout.h"
#implementation XYZShout
-(void) saySomething:(NSString *)greet {
NSString *upperGreet = [greet uppercaseString];
[super saySomething:upperGreet]; // this is where I get the error mentioned above
}
#end
Got it working ! Thanks to #MatthewD , #trojanfoe , #JFS for your big help :)
It looks like you are testing inheritance so I will assume that XYZShout is supposed to be derived from XYZPerson. If so follow the suggestion from #JFS and make sure it does actually derive:
XYZShout.h:
#import <Foundation/Foundation.h>
#import "XYZPerson.h"
#interface XYZShout : XYZPerson
- (void)saySomething:(NSString *)greet;
#end
And also correct the definition of saySomething in XYZPerson (you missed off the parameter):
XYZPerson.h:
#import <Foundation/Foundation.h>
#interface XYZPerson : NSObject
#property NSString *firstName;
#property NSString *secondName;
#property NSDate *dob;
- (void)saySomething:(NSString *)greet;
// ^^^^^^^^^^^^^^^^^
- (void)sayHello;
#end
(Moved from comments into an answer...)
MatthewD: What happens if you change - (void) saySomething; in XYZPerson.h to - (void) saySomething:greet;?
Raj0689: Why does it run when I change it to saySomething:greet and not saySomething ? Since greet is defined only along with saySomething !!
When you call a method, the compiler needs to locate the signature of that method so it can verify that the method is being called correctly. The signature includes the method name and the number and types of parameters. The usual way of providing method signatures is by importing the header file that defines those signatures.
So, in XYZShout.m where you call:
[super saySomething:upperGreet];
The compiler searches XYZShout.h, which is imported by XYZShout.m, and XYZPerson.h, which is imported by XYZShout.h. In XYZShout.h, the following method was being found:
-(void) saySomething;
This matches the called method in name, but not in parameters, so the compiler does not consider this a match. No other definitions of saySomething are found anywhere, so it gives an error instead.
Please make sure to set the XYZShout.h interface to #interface XYZShout : XYZPerson?

My own method isn't being found by the compiler

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];

Objective-C dot syntax or property value?

I keep reading that dot syntax is possible but I keep getting errors that the struct does not contain members I am referencing. Perhaps its not the dot syntax so I have included details of what I am doing in hopes of a solution:
// MobRec.h - used as the objects in the MobInfo array
#import <Foundation/Foundation.h>
#interface MobRec : NSObject {
#public NSString *mName;
#public int mSpeed;
}
#property (nonatomic, retain) NSString *mName;
#property (nonatomic) int mSpeed;
// MobDefs.h - array of MobRecords
#interface Mobdefs : NSObject {
#public NSMutableArray *mobInfo;
}
#property(assign) NSMutableArray *mobInfo; // is this the right property?
-(void) initMobTable;
#end
// MobDefs.m
#import "Mobdefs.h"
#import "Mobrec.h"
#implementation Mobdefs
#synthesize mobInfo;
-(void) initMobTable
{
// if I use traditional method I get may not respond
[mobInfo objectAtIndex:0 setmName: #"doug"];
// if I use dot syntax I get struct has no member named mName
mobInfo[1].MName = #"eric";
}
// main.h
MobDefs *mobdef;
// main.m
mobdef = [[Mobdefs alloc] init];
[mobdef initMobTable];
although both methods should work I get erros on both. What am I doing wrong? My best thoughts have been that I am using the wrong #property but I think I have tried all. I am performing alloc in main. Ideally I would like to for this use dot syntax and cant see why its not allowing it.
A couple of things: (edit: original point #1 removed due to error)
Although the dot syntax is supported, the array index syntax for NSArray is not. Thus, your call to mobInfo[1] will not be the same as [mobInfo objectAtIndex:1]; Instead, mobInfo will be treated as a simple C-style array, and that call would be almost guaranteed to result in a crash.
You should not define variables in your header file as you do in main.h. The line MobDefs *mobdef; belongs somewhere in main.m.
edit: Here is how it should look:
MobRec.h
#interface MobRec : NSObject {
NSString *mName;
int mSpeed;
}
#property (nonatomic, retain) NSString *mName;
#property (nonatomic) int mSpeed;
MobRec.m
#implementation MobRec
#synthesize mName;
#synthesize mSpeed;
#end
MobDefs.h
#interface MobDefs : NSObject {
NSMutableArray *mobInfo;
}
#property(assign) NSMutableArray *mobInfo;
-(void) initMobTable;
#end
MobDefs.m
#import "MobDefs.h"
#import "MobRec.h"
#implementation MobDefs
#synthesize mobInfo;
-(void) initMobTable
{
// option 1:
[(MobRec*)[mobInfo objectAtIndex:0] setMName:#"doug"];
// option 2:
(MobRec*)[mobInfo objectAtIndex:0].mName = #"eric";
// option 3:
MobRec *mobRec = [mobInfo objectAtIndex:0];
mobRec.mName = #"eric";
}
main.m
MobDef *mobdef = [[MobDefs alloc] init];
[mobdef initMobTable];
...
[mobdef release]; // don't forget!
You need to either cast the object returned by -objectAtIndex:, or use a method call on it:
[[mobInfo objectAtIndex: 0] setMName: #"doug"];
or
((Mobrec *) [mobInfo objectAtIndex: 0]).MName = #"doug";
[mobInfo objectAtIndex:0 setmName: #"doug"];
There is no objectAtIndex:setmName method, so you're going to have to explain what you think this is even supposed to do.
mobInfo[1].MName = #"eric";
Use objectAtIndex to look something up in an NSArray object.