Property not found although it should be - objective-c

This is a weird bug.
I have this in the header:
#import "UIKit/UIKit.h"
#interface ProxyProfileObject : NSObject <NSCoding> {
NSString *profileName;
NSString *ipAddress;
NSString *port;
}
-(void) setProfileName:(NSString *)string;
-(NSString*) getProfileName;
-(void) setIP:(NSString *)string;
-(NSString*) getIP;
-(void) setPort:(NSString *)string;
-(NSString*) getPort;
#end
And this in the implementation:
#import "ProxyProfileObject.h"
#interface ProxyProfileObject()
#end
#implementation ProxyProfileObject
-(void) setProfileName:(NSString *)string{
profileName = string;
}
-(NSString*) getProfileName{
return profileName;
}
-(void) setIP:(NSString *)string{
ipAddress = string;
}
-(NSString*) getIP{
return ipAddress;
}
-(void) setPort:(NSString *)string{
port = string;
}
-(NSString*) getPort{
return port;
}
// Encoding stuff
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.profileName = [decoder decodeObjectForKey:#"profileName"];
self.port = [decoder decodeObjectForKey:#"port"];
self.ipAddress = [decoder decodeObjectForKey:#"ip"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:profileName forKey:#"profileName"];
[encoder encodeObject:ipAddress forKey:#"ip"];
[encoder encodeObject:port forKey:#"port"];
}
#end
I am not sure why it does this. It shouldn't do it as ipAddress is the same thing as port or profile name.
Those are the two files. You can now see by yourself how ipAddress doesn't work.

In your header NSString *ipAddress declares an ivar.
I the implementation self.ipAddress refers to a property.
You declared no such property. Hence the error.
After the edit your problem becomes apparent:
You do not declare a method -(void)setIpAddress:(NSString *)address;
That's what would make Xcode allow you to use property syntax (dot notation) for the setter—even though it's not an actual property.

You are declaring instance variables, not properties. Just put the #property directive in front of the lines.
#interface ProxyProfileObject : NSObject <NSCoding> {}
#property NSString *profileName;
#property NSString *ipAddress;
#property NSString *port;
Edit: Don't write explicit getters and setters. Use the (error-free) synthesized accessors provided by the #property declaration.

Related

How to implement initWith method passing custom object as an argument?

For instance I have following class:
#interface PrivateInfo: NSObject
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi;
#end
#implementation PrivateInfo
{
#private
NSString *name;
NSString *creditCardID;
}
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi
{
// how to init current object with passed pi ?
}
- (PrivateInfo *)copy
{
// how to create a copy of current object?
}
#end
Assuming ARC:
.h:
#interface PrivateInfo: NSObject <NSCopying>
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi;
#end
.m:
#implementation PrivateInfo {
NSString *name;
NSString *creditCardID;
}
- (instancetype)initWithPrivateInfoObject:(PrivateInfo *)pi
{
self = [super init];
if (self) {
name = pi->name;
creditCardID = pi->creditCardID;
}
return self;
}
- (PrivateInfo *)copyWithZone:(NSZone *)zone {
PrivateInfo *res = [[PrivateInfo alloc] initWithPrivateInfoObject:self];
return res;
}
#end
You don't need #private in the #implementation block for declaring ivars. And use the NSCopying protocol is you want to copy objects.

initWithCoder: not visible in NSObject?

I have an interface:
#import <Foundation/Foundation.h>
#interface Picture : NSObject;
#property (readonly) NSString *filepath;
- (UIImage *)image;
#end
and implementation:
#import "Picture.h"
#define kFilepath #"filepath"
#interface Picture () <NSCoding> {
NSString *filepath;
}
#end
#implementation Picture
#synthesize filepath;
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:filepath forKey:kFilepath];
}
- (UIImage *)image {
return [UIImage imageWithContentsOfFile:filepath];
}
#end
I get error: ARC issue - No visible #interface for 'NSObject' declares the selector' initWithCoder:'
Is there something different about NSCoding when using ARC?
Thanks
There's nothing different between ARC and manual reference counting. NSObject doesn't conform to NSCoding and therefore doesn't supply -initWithCoder: or -encodeWithCoder:. Just don't call through to the superclass implementations of those methods.
- (id)initWithCoder: (NSCoder *)aCoder {
self = [super init];
if (self) {
[aCoder decodeObject: filepath forKey: kFilepath];
}
return self;
}

How to Create a Class(es) for storing multi-level data?

I need to store multi-level data for a flash card app I'm writing and I could use some help figuring out 1) How to manage the data and 2) How to store it.
The data is broken down like this:
a) Card contains 2 strings
b) Pack contains a String "PackName" and an array of Cards
c) Deck contains a String "DeckName" and an array of Packs
Right now I have 3 Classes: Card, Pack, Deck.
//Card.h
#interface Card : NSObject {
NSString *primaryPhrase;
NSString *secondaryPhrase;
}
#property (nonatomic,retain)NSString *primaryPhrase;
#property (nonatomic,retain)NSString *secondaryPhrase;
#end
Card.m
#implementation Card
#synthesize primaryPhrase;
#synthesize secondaryPhrase;
-(id)init{
if(self=[super init]){
}
return self;
}
#end
Pack.h
#interface Pack : NSObject{
NSString *packName;
NSMutableArray *cards; //array of card classes
BOOL isInUse;
}
#property (nonatomic,retain)NSMutableArray *cards;
#property (nonatomic,retain)NSString *packName;
#property (nonatomic,assign)BOOL isInUse;
#end
Pack.m
#implementation Pack
#synthesize packName;
#synthesize cards;
#synthesize isInUse;
-(id)init{
if(self=[super init]){
self.isInUse=YES;
}
return self;
}
#end
Deck.h
#interface Deck : NSObject <NSCoding>{
NSString *deckName;
NSMutableArray *packs; //array of pack classes
NSString *primaryLang;
NSString *secondaryLang;
}
#property (nonatomic,retain)NSMutableArray *packs;
#property (nonatomic,retain)NSString *deckName;
#property (nonatomic,retain)NSString *primaryLang;
#property (nonatomic,retain)NSString *secondaryLang;
- (void) encodeWithCoder:(NSCoder*)encoder;
- (id) initWithCoder:(NSCoder*)decoder;
#end
Deck.m
#import "Deck.h"
#implementation Deck
#synthesize packs;
#synthesize deckName;
#synthesize primaryLang;
#synthesize secondaryLang;
//Default settings for each new Deck
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:packs forKey:#"packs"];
[encoder encodeObject:deckName forKey:#"deckName"];
[encoder encodeObject:primaryLang forKey:#"primaryLang"];
[encoder encodeObject:secondaryLang forKey:#"secondaryLang"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
packs=[decoder decodeObjectForKey:#"packs"];
deckName=[decoder decodeObjectForKey:#"deckName"];
primaryLang=[decoder decodeObjectForKey:#"primaryLang"];
secondaryLang=[decoder decodeObjectForKey:#"secondaryLang"];
}
return self;
}
#end
Then I use an NSMutableArray "allDecks" to hold Decks, which in turn contain Cards, but I haven't even been able to get this to work (no errors, but "pack name" is always null):
for(int i=0; i<=2; i++){
Deck *newdeck=[[Deck alloc]init];
[globDat.allDecks addObject:newdeck];
}
((Deck *)[globDat.allDecks objectAtIndex:0]).deckName=#"DeckName 0";
((Deck *)[globDat.allDecks objectAtIndex:1]).deckName=#"DeckName 1";
((Deck *)[globDat.allDecks objectAtIndex:2]).deckName=#"DeckName 2";
for(int i=0; i<=2; i++){
Pack *newpack=[[Pack alloc] init];
[((Deck *)[globDat.allDecks objectAtIndex:i]).packs addObject:newpack];
}
for(int j=0; j<+2; j++){
((Pack *)[((Deck *)[globDat.allDecks objectAtIndex:0]).packs objectAtIndex:j]).packName=#"pack name";
}
NSLog(#"*** NIL sample pack name=%#",((Pack *)[((Deck *)[globDat.allDecks objectAtIndex:0]).packs objectAtIndex:0]).packName);
//always returns null
It's pretty cumbersome to work the structure.
Is this the best way to manage this data?
Plus, the encoding doesn't seem to save the embedded arrays (Pack and Card).
I'll post this as an answer, though it's really an opinion.
I would use Core Data for the model layer. You will not need to deal with serializing your object graph as your are now. Rather, the object graph persistence is largely handled by the framework. There is a learning curve - per Apple, it is not an "entry-level technology" - but it will be much more manageable in the long run.
As for the issue with serialization of the arrays in your object graph, NSMutableArray conforms to the NSCoding protocol; there is some other issue. Instead of:
packs=[decoder decodeObjectForKey:#"packs"];
don't you mean:
self.packs = [decoder decodeObjectForKey:#"packs"];
or
packs = [[decoder decodeObjectForKey:#"packs"] retain];
(I'm assuming you're not using ARC...)
Honestly I would continue with the way you are doing things. The reason that Pack and Card aren't being saved is because they each need to implement the encodeWithCoder: and initWithCoder: methods.
Card.h
#interface Card : NSObject
#property (nonatomic,retain)NSString *primaryPhrase;
#property (nonatomic,retain)NSString *secondaryPhrase;
#end
Card.m
#implementation Card
#synthesize primaryPhrase, secondaryPhrase;
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:primaryPhrase forKey:#"primaryPhrase"];
[encoder encodeObject:secondaryPhrase forKey:#"secondaryPhrase"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
primaryPhrase=[decoder decodeObjectForKey:#"primaryPhrase"];
secondaryPhrase=[decoder decodeObjectForKey:#"secondaryPhrase"];
}
return self;
}
#end
Pack.h
#interface Pack : NSObject
#property (nonatomic,retain)NSMutableArray *cards;
#property (nonatomic,retain)NSString *packName;
#property (nonatomic,assign)BOOL isInUse;
#end
Pack.m
#implementation Pack
#synthesize packName, cards, isInUse;
-(id)init{
if(self=[super init]){
self.isInUse=YES;
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:packName forKey:#"packName"];
[encoder encodeObject:cards forKey:#"cards"];
[encoder encodeObject:[NSNumber numberWithBool:isInUse] forKey:#"isInuse"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
packName=[decoder decodeObjectForKey:#"packName"];
cards=[decoder decodeObjectForKey:#"cards"];
isInUse=[[decoder decodeObjectForKey:#"isInUse"] boolValue];
}
return self;
}
#end
Deck.h
#interface Deck : NSObject <NSCoding>
#property (nonatomic,retain)NSMutableArray *packs;
#property (nonatomic,retain)NSString *deckName;
#property (nonatomic,retain)NSString *primaryLang;
#property (nonatomic,retain)NSString *secondaryLang;
#end
Deck.m
#import "Deck.h"
#implementation Deck
#synthesize packs, deckName, primaryLang, secondaryLang;
-(id)init{
if(self=[super init]){
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:packs forKey:#"packs"];
[encoder encodeObject:deckName forKey:#"deckName"];
[encoder encodeObject:primaryLang forKey:#"primaryLang"];
[encoder encodeObject:secondaryLang forKey:#"secondaryLang"];
}
-(id)initWithCoder:(NSCoder*)decoder{
if(self=[super init]){
packs=[decoder decodeObjectForKey:#"packs"];
deckName=[decoder decodeObjectForKey:#"deckName"];
primaryLang=[decoder decodeObjectForKey:#"primaryLang"];
secondaryLang=[decoder decodeObjectForKey:#"secondaryLang"];
}
return self;
}

How to create a class for a type of object?

Ok, I'm still getting used to how Objective-c works.
Lets suppose I'm making a todo list app. Instead of just reading from a plist and loading it into a table, some people say that you should create a class, lets call it ToDo that contains for example:
NSString *title;
NSString *description;
Ok, fine. Now how would I use such a class to load my data from a plist or something? I don't understand how creating a little class helps. Can anyone explain to me how this works?
Just creat a subclass of NSObject with NSCoding protocol(if you want to save it in a plist)
Example: *.h file
#interface ToDo : NSObject <NSCoding> {
NSString *title;
NSString *toDoDescription;
}
#property (copy) NSString *title;
#property (copy) NSString *toDoDescription;
#end
Example: *.m file
#implementation ToDo
#synthesize title, toDoDescription;
- (id)init
{
if ((self = [super init])) {
[self setTitle:#"none"];
[self setToDoDescription:#"none"];
}
return self;
}
- (void)dealloc
{
[title release];
[toDoDescription release];
[super dealloc];
}
// Next two methods and coding protocol are needed to save your custom object into plist
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super init])) {
title = [[aDecoder decodeObjectForKey:#"title"] copy];
toDoDescription = [[aDecoder decodeObjectForKey:#"toDoDescription"] copy];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:title forKey:#"title"];
[aCoder encodeObject:toDoDescription forKey:#"toDoDescription"];
}
#end
Use NSKeyedArchiver to covert your object into NSData. And then add it to a plist.

Observing a Change to ANY Class Property in Objective-C

Put simply, is there a way to receive a general notification when any property in an Objective-C class is changed? I know I can use KVO to monitor particular property changes, but I have the need to call a particular method whenever any setProperty: message is sent to my class. I want to be able to receive a generic notification without any concern about which property in particular was modified.
If it helps to clarify why I want to do this, I am making use of some fast table scrolling code found here: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/
Part of the process of accomplishing this is that whenever a property in a table view cell is modified, [ self setNeedsDisplay ] needs to be called. I'd rather not have to override the setter methods for every property in my class just to make this call.
As Chuck notes, you can create a dependent key, or of course you can directly observe all the properties (which is less work than overloading the setters).
Using the Objective-C runtime, if you exclusively use properties, you can automate this process using class_copyPropertyList(). But I'd probably only do this if this problem comes up a bit for you. If you only have one instance of this problem, it's probably easier and safer and more maintainable just to directly observe the list of properties unless you feel like working in the ObjC runtime.
Here's an example built off of Chuck and Rob's suggestions:
DrakeObject.h
#interface DrakeObject : NSObject
#property (nonatomic, strong) NSNumber *age;
#property (nonatomic, strong) NSNumber *money;
#property (nonatomic, strong) NSString *startPosition;
#property (nonatomic, strong) NSString *currentPosition;
#property (nonatomic, strong, readonly) id propertiesChanged;
#end
DrakeObject.m
#implementation DrakeObject
- (instancetype)init {
self = [super init];
if (self) {
self.age = #25;
self.money = #25000000;
self.startPosition = #"bottom";
self.currentPosition = #"here";
}
return self;
}
- (id)propertiesChanged {
return nil;
}
+(NSSet *)keyPathsForValuesAffectingPropertiesChanged {
return [NSSet setWithObjects:#"age", #"money", #"startPosition", #"currentPosition", nil];
}
observing propertiesChanged will let us know anytime a property has changed.
[self.drakeObject addObserver:self
forKeyPath:#"propertiesChanged"
options:NSKeyValueObservingOptionNew
context:nil];
Not exactly. You can create a dependent key that depends on every property you wish to expose and then observe that. That's about as close as you'll get, I think.
Here an example of code. I have a general object and dother object. Dother object has to save his state on change each property.
#import <Foundation/Foundation.h>
#interface GeneralObject : NSObject
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary;
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary;
- (NSDictionary *)dictionaryValue;
- (NSArray *)allPropertyNames;
#end
implementation
#import "GeneralObject.h"
#import <objc/runtime.h>
#implementation GeneralObject
#pragma mark - Public
+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary {
return [[self alloc] initWithDictionary:aDictionary];
}
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary {
aDictionary = [aDictionary clean];
for (NSString* propName in [self allPropertyNames]) {
[self setValue:aDictionary[propName] forKey:propName];
}
return self;
}
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSArray *propertyNames = [self allPropertyNames];
id object;
for (NSString *key in propertyNames) {
object = [self valueForKey:key];
if (object) {
[result setObject:object forKey:key];
}
}
return result;
}
- (NSArray *)allPropertyNames {
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *array = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[array addObject:name];
}
free(properties);
return array;
}
#end
and after all we have dother class, which should save his state on each change of any property
#import "GeneralObject.h"
extern NSString *const kUserDefaultsUserKey;
#interface DotherObject : GeneralObject
#property (strong, nonatomic) NSString *firstName;
#property (strong, nonatomic) NSString *lastName;
#property (strong, nonatomic) NSString *email;
#end
and implementation
#import "DotherObject.h"
NSString *const kUserDefaultsUserKey = #"CurrentUserKey";
#implementation DotherObject
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
if (self = [super initWithDictionary:dictionary]) {
for (NSString *key in [self allPropertyNames]) {
[self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
}
}
return self;
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context {
NSDictionary *dict = [self dictionaryValue];
[[NSUserDefaults standardUserDefaults] setObject:dict forKey:kUserDefaultsUserKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSString *)description {
return [NSString stringWithFormat:#"%#; dict:\n%#", [super description], [self dictionaryValue]];
}
#end
Happy coding!