I am a complete beginner at Objective c and I am trying to complete a challenge in the book "iOS programming: The big Nerd Ranch guide".
I'm trying to put an object called item (of the class BNRItem) into an NSMutableArray called subItems which is part of an object called container (of the class BNRContainer, a subclass of BNRItem with the addition of the NSMutableArray to hold BNRItems). BNRItem works fine.
The code is as follows:
BNRContainer.h
#import <Foundation/Foundation.h>
#import "BNRItem.h"
#interface BNRContainer : BNRItem
{
NSMutableArray *subItems;
}
BNRContainer.m
- (id)init
{
return [self initWithItemName:#"Container"
valueInDollars:0
serialNumber:#""];
}
- (void)setSubItems:(BNRItem*)item
{
[subItems addObject:item];
}
Main.m
#import <Foundation/Foundation.h>
#import "BNRItem.h"
#import "BNRContainer.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
BNRItem *item = [[BNRItem alloc] init];
BNRContainer *container = [[BNRContainer alloc] init];
[container setSubItems:item]
}
return 0;
}
At the line [container setSubItems:item] I get the error: No visible #interface for container declares the selector setSubItems
The setter method setSubItems doesn't code complete (although other setters do, and work fine).
Am I doing something simple wrong? A simple explanation would be very appreciated!
Update BNRContainer.h:
#import <Foundation/Foundation.h>
#import "BNRItem.h"
#interface BNRContainer : BNRItem
{
NSMutableArray *subItems;
}
- (void)setSubItems:(BNRItem*)item;
#end
(Dunno why Fred deleted his answer.)
In order for Xcode to generate the getters/setters for subItems, you have to actually declare the property for it in your interface. Something like this:
#import <Foundation/Foundation.h>
#import "BNRItem.h"
#interface BNRContainer : BNRItem
#property (strong, nonatomic) NSMutableArray *subItems;
#end
Additionally, you aren't ever actually alloc/initing your array, and the current logic for setSubItems: will not do what it sounds like it would do. This function would add the array passed as a parameter as an object within SubItems. If you're trying to add items from an array to subitems, then you should be using:
[myMutableArray addObjectsFromArray:<#(NSArray *)#>];
Related
I want to inherit my base class properties and methods which will be used by my several derived classes. I want these properties and methods to be exactly protected so that they will only be visible in derived class and not to any external class. But it always gives me some errors.
#interface BasePerson : NSObject
#end
#interface BasePerson ()
#property (nonatomic, strong) NSMutableArray<Person*>* savedPersons;
#property (nonatomic) BOOL shouldSavePerson;
#end
#interface DerivedPerson1 : BasePerson
#end
#implementation DerivedPerson1
- (instancetype)init
{
if (self = [super init]) {
self.savedPersons = [NSMutableArray array];
self.shouldSavePerson = NO;
}
return self;
}
It always gives me an error that
Property 'savedPersons' not found on object of type 'DerivedPerson1 *'
Property 'shouldSavePerson' not found on object of type 'DerivedPerson1 *'
How i can make use of inheritance in Objective C, I don't want savedPersons and shouldSavePerson properties to be visible to external classes. I only want them to visible in my base class and all the derived classes.
Any help will be great. Thanks
This is not something that the objectiveC really support. There are some ways though. So lets see.
If you put a property in the source file class extension then it is not exposed and you can not access it in the subclass either.
One way is to put all of the subclasses into the same source file as the base class. This is not a good solution at all as you do want to have separate files for separate classes.
It seems logical to import the BaseClass.m in the SubClass source file but that will produce a linker error saying that you have duplicate symbols.
And the solution:
Separate the extension into a separate header. So you have a MyClass
Header:
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
#end
Source:
#import "MyClass.h"
#import "MyClassProtected.h"
#implementation MyClass
- (void)foo {
self.someProperty = #"Some text from base class";
}
#end
Then you create another header file (only the header) MyClassProtected.h which has the following:
#import "MyClass.h"
#interface MyClass ()
#property (nonatomic, strong) NSString *someProperty;
#end
And the subclass MyClassSubclass
Header:
#import "MyClass.h"
#interface MyClassSubclass : MyClass
#end
And the source:
#import "MyClassSubclass.h"
#import "MyClassProtected.h"
#implementation MyClassSubclass
- (void)foo {
self.someProperty = #"We can set it here as well";
}
#end
So now if the user MyClassSubclass he will not have the access to the protected property which is essentially what you want. But the downside is the user may still import MyClassProtected.h after which he will have the access to the property.
Objective-C doesn't have member access control for methods, but you can emulate it using header files.
BasePerson.h
#interface BasePerson : NSObject
#property (strong,nonatomic) SomeClass *somePublicProperty;
-(void) somePublicMethod;
#end
BasePerson-Private.h
#import "BasePerson.h"
#interface BasePerson ()
#property (nonatomic, strong) NSMutableArray<Person*>* savedPersons;
#property (nonatomic) BOOL shouldSavePerson;
#end
BasePerson.m
#import "BasePerson-Private.h"
...
DerivedPerson1.h
#import "BasePerson-Private.h"
#inteface DerivedPerson1 : BasePerson
...
#end
Now any class that #imports BasePerson.h will only see the public methods. As I said though, this is only emulating access control since if a class #imports *BasePerson-Private.h" they will see the private members; this is just how C/Objective-C is.
We can achieve using #protected access specifier
#interface BasePerson : NSObject {
#protected NSMutableArray *savedPersons;
#protected BOOL shouldSavePerson;
}
DerivedPerson1.m
#implementation DerivedPerson1
- (instancetype)init
{
if (self = [super init]) {
self->savedPersons = [NSMutableArray array];
self->shouldSavePerson = NO;
}
return self;
}
#end
OtherClass.m
#import "OtherClass.h"
#import "BasePerson.h"
#implementation OtherClass
- (void)awakeFromNib {
BasePerson *base = [[BasePerson alloc]init];
base->savedPersons = #[];//Getting Error. Because it is not a subclass.
}
#end
Somehow i can not change the properties of my custom object anymore.
I used Xcode 6 to create my project and moved to XCode 7 now. It told me to "update to recommended settings" and i did it.
Object.h
#interface Object : NSObject <NSCoding>
#property (nonatomic, copy) NSString *ID;
ViewController.m
#import "Object.h"
#interface ViewController ()
#end
Object *myObject;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
myObject = [[Object alloc] init];
}
- (IBAction)editProperty:(id)sender {
myObject.ID = _textfield.text;
NSLog(#"ID : %#",myObject.ID);
}
This all worked perfectly fine, but now myObject.ID is always (null).....
When i write this code:
myObject.ID = _textfield.text;
NSLog(#"ID : %#",myObject.ID);
inside viewDidLoad it works...
One major issue is this line:
Object *myObject;
What is myObject? It is just floating free. It is not a property. It is not an instance variable. So what is it? That line is legal but it makes little sense.
Two class access each other by importing their head file(via #import ) causes error?
I have been always suggested to use #class className in the header file if needing to access other classes . But i've rarely got any Principle of why should i do this. There is a small sample below, which triggers errors like" UNKnown type name ‘xxxClass’”. Does that mean the “#import” doesn’t copy the code here ? If not so, why is it unable to detect the ‘xxxClass’
I think this is not a cycle retain problem, but What is the principle of the error ?
============================ Person ===============================
Person.h
#import <Foundation/Foundation.h>
#import "Dog.h"
//#class Dog;
#interface Person : NSObject
#property (nonatomic, strong) Dog *dog; //Error :UNKnown type name ‘Dog'
#end
Person.m
#import "Person.h"
#implementation Person
- (void)dealloc{ NSLog(#"Person--dealloc");}
#end
============================ Dog ===============================
Dog.h
#import <Foundation/Foundation.h>
#import "Person.h"
#interface Dog : NSObject
#property (nonatomic, weak) Person *person; //Error:UNKnown type name‘Person'
#end
Dog.m
#import "Dog.h"
#implementation Dog
- (void)dealloc{ NSLog(#"Dog--dealloc");}
#end
============================ main ===============================
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"
int main()
{
Person *p = [[Person alloc] init];
Dog *d = [[Dog alloc] init];
p.dog = d;
d.person = p;
return 0;
}
#class Dog means that , when its starting compiling , you are telling compiler that it will have this class ( later ) and does not have to include/compile at the moment ,
you must use #class in header ,
if you want to manipulate this object from implementation ( .m file ) , then , you can include another-classes header file (.h) directly in .m file
//Person.h
#import <Foundation/Foundation.h>
#class Dog;
#interface Person : NSObject
#property (nonatomic, strong) Dog *dog;
#end
//Person.m
#import "Person.h"
#implementation Person
- (void)dealloc{ NSLog(#"Person--dealloc");}
#end
//Dog.h
#import <Foundation/Foundation.h>
#class Person;
#interface Dog : NSObject
#property (nonatomic, weak) Person *person;
#end
//Dog.m
#import "Dog.h"
// **IF YOU WANT TO HAVE ACCESS TO THE PERSONS OBJECT AND ITS PROPERTIES , INCLUDE ITS HEADER HERE**
#implementation Dog
- (void)dealloc{ NSLog(#"Dog--dealloc");}
#end
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"
int main()
{
Person *p = [[Person alloc] init];
Dog *d = [[Dog alloc] init];
p.dog = d;
d.person = p;
return 0;
}
Basically, #import makes the compile get the entire text of the specified file into the one where the statement was written. When you have circular #import statements the compiler becomes confused and throws an error. That is why you should use #class to break that cycle.
Very basic question here...
I have 2 classes and I want to keep an NSArray in one class and access it in different class.
Questions.h
#import <Foundation/Foundation.h>
#interface Questions : NSObject
#property NSMutableArray *questions;
-(void) questionMethod;
#end
Questions.m
#import "Questions.h"
#implementation Questions
-(void) questionMethod {
NSArray *questionBank = [4,5,6];
}
#end
ViewController.m
#import "Questions.h"
-(void)generateRandomQuestionOrder {
Questions *questions = [[Questions alloc] init];
}
How do I count the values of questionBank array in generateRandomQuestionOrder method?
Not the best naming convention but take a look at the following example. You declare a public property in the Questions object and access it from the controller after you initialised a new object there. You may consider declaring it readonly and set it to readwrite in the private interface extension.
Questions.h
#import <Foundation/Foundation.h>
#interface Questions : NSObject
#property NSMutableArray *questions;
#end
Questions.m
#import "Questions.h"
#implementation Questions
-(id) init {
if (self = [super init]) {
_questions = #[4,5,6];
}
return self;
}
#end
ViewController.m
#import "Questions.h"
-(void)generateRandomQuestionOrder {
Questions *theQuestions = [[Questions alloc] init];
NSLog(#"%#", [theQuestions.questions description]);
}
I am trying to call a method in my data controller object to load the data for my application, but for some reason it is not being called. Below is what I have done to initialize it. Any help would be greatly appreciated.
ViewController:
header file:
#import <UIKit/UIKit.h>
#class DetailViewController;
#class DataController;
#import <CoreData/CoreData.h>
#import "JointCAD.h"
#interface TableViewController : UITableViewController {
}
#property (nonatomic, assign) DataController *dataController;
#end
implementation file:
#import "TableViewController.h"
#import "DataController.h"
#implementation TableViewController
#synthesize dataController;
- (void)viewDidLoad {
[dataController refreshData];
}
#end
Data Controller:
header file:
#import <Foundation/Foundation.h>
#import "JointCAD.h"
#import "JointCADXMLParser.h"
#import "TFHpple.h"
#interface DataController : NSObject {
TFHpple *xpathParser;
}
- (void)refreshData;
- (void)initXMLParser;
- (void)noCallsMessage;
- (void)noInternetMessage;
#end
implementation file:
#import "DataController.h"
#implementation DataController
XMLParser *xmlParser;
- (void)refreshData {
NSLog("Some Method");
}
Is 'dataController' Object being set by some other class? - I believe that's why you have set it as a property? Right?
If No, then Remove the property,#synthesize of 'dataController' and try simple allocation of your 'dataController' object and then try calling your method.
Hope it helps.
You either need to initialize "DataController" prior to actually calling one of it's methods, or you need to make the method, "refreshData" a class by changing it's "-" to a "+".
If you need an instance callback instead. You need to rewrite "viewDidLoad" like this:
- (void)viewDidLoad {
DataController *dataController = [[DataController alloc] init];
[dataController refreshData];
}
And get rid of the property declaration of dataController because you haven't initialized it. If you would prefer a property declaration instead, simply allocate the viewcontroller prior to calling a function from it.
- (void)viewDidLoad {
dataController = [[DataController alloc] init];
[dataController refreshData];
}
One last thing to note is that I (and probably Ray) assume that you're using a storyboard configuration. If you are using a xib configuration, you need to add initWithNibName: to each initialization of the view controller.
I hope that's helpful!