No visible #interface for 'BNRItemStore' declares the selector 'deleteImageForKey;' - objective-c

I'm new to objective C and have been learning with the book "IOS Programming Big Nerd Ranch Guide 4th edition". I keep getting a recurring error and I have been working to resolve it for the last few hours. After researching for a bit, I came here. Any help would be greatly appreciated.
"No visible #interface for 'BNRItemStore' declares the selector 'deleteImageForKey;'"
BNRItemStore.h
#import <Foundation/Foundation.h>
#class BNRItem;
#interface BNRItemStore : NSObject
#property (nonatomic, readonly) NSArray *allItems;
// Notice that this is a class method and prefixed with a + instead of a -
+ (instancetype)sharedStore;
- (BNRItem *)createItem;
- (void)removeItem:(BNRItem *)item;
- (void)moveItemAtIndex:(NSInteger)fromIndex
toIndex:(NSInteger)toIndex;
#end
BNRItemStore.m
#import "BNRItemStore.h"
#import "BNRItem.h"
#interface BNRItemStore ()
#property (nonatomic) NSMutableArray *privateItems;
#end
#implementation BNRItemStore
+ (instancetype)sharedStore
{
static BNRItemStore *sharedStore = nil;
// Do I need to create a sharedStore?
if (!sharedStore) {
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
// If a programmer calls [[BNRItemStore alloc] init], let him
// know the error of his ways
- (instancetype)init
{
#throw [NSException exceptionWithName:#"Singleton"
reason:#"Use +[BNRItemStore sharedStore]"
userInfo:nil];
return nil;
}
// Here is the real (secret) initializer
- (instancetype)initPrivate
{
self = [super init];
if (self) {
_privateItems = [[NSMutableArray alloc] init];
}
return self;
}
- (NSArray *)allItems
{
return [self.privateItems copy];
}
- (BNRItem *)createItem
{
BNRItem *item = [BNRItem randomItem];
[self.privateItems addObject:item];
return item;
}
- (void)removeItem:(BNRItem *)item
{
NSString *key = item.itemKey;
if (key) {
[[BNRItemStore sharedStore] deleteImageForKey:key];
}
[self.privateItems removeObjectIdenticalTo:item];
}
- (void)moveItemAtIndex:(NSInteger)fromIndex
toIndex:(NSInteger)toIndex
{
if (fromIndex == toIndex) {
return;
}
// Get pointer to object being moved so you can re-insert it
BNRItem *item = self.privateItems[fromIndex];
// Remove item from array
[self.privateItems removeObjectAtIndex:fromIndex];
// Insert item in array at new location
[self.privateItems insertObject:item atIndex:toIndex];
}
#end
BNRImageStore.h
#import <Foundation/Foundation.h>
#interface BNRImageStore : NSObject
+ (instancetype)sharedStore;
- (void)setImage:(UIImage *)image forKey:(NSString *)key;
- (UIImage *)imageForKey:(NSString *)key;
- (void)deleteImageForKey:(NSString *)key;
#end
BNRImageStore.m
#import "BNRImageStore.h"
#interface BNRImageStore ()
#property (nonatomic, strong) NSMutableDictionary *dictionary;
#end
#implementation BNRImageStore
+ (instancetype)sharedStore
{
static BNRImageStore *sharedStore = nil;
if (!sharedStore) {
sharedStore = [[self alloc] initPrivate];
}
return sharedStore;
}
// No one should call init
- (instancetype)init
{
#throw [NSException exceptionWithName:#"Singleton"
reason:#"Use +[BNRImageStore sharedStore]"
userInfo:nil];
return nil;
}
// Secret designated initializer
- (instancetype)initPrivate
{
self = [super init];
if (self) {
_dictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)setImage:(UIImage *)image forKey:(NSString *)key
{
self.dictionary[key] = image;
}
- (UIImage *)imageForKey:(NSString *)key
{
return self.dictionary[key];
}
- (void)deleteImageForKey:(NSString *)key
{
if (!key) {
return;
}
[self.dictionary removeObjectForKey:key];
}
#end

You are declaring the method deleteImageForKey in your BNRImageStore class, not in your BNRItemStore.
Check your implementation of removeItem: in BNRItemStore.m
- (void)removeItem:(BNRItem *)item
{
NSString *key = item.itemKey;
if (key) {
[[BNRItemStore sharedStore] deleteImageForKey:key];
}
[self.privateItems removeObjectIdenticalTo:item];
}
I assume you meant to refer to "BNRImageStore" and not BNRItemStore.
Apart from the typo, you should understand that Objective-C objects respond to selectors. Every Objective-C object has an array of selectors that it responds to. When you see the error: "No visible #interface for 'BNRItemStore' declares the selector 'deleteImageForKey;'" you should understand that the compiler does not see the selector you specified as being understood by the class in the error.

Related

Error when using method "no Known class method for selector"

The class "PlayingCardDeck" inherits "Deck" class can not put the method
- (void) addCard:(Card *)card atTop:(BOOL)atTop
Xcode accuses me the following error:
no Known class method for selector 'addCard:atTop'
Class: PlayingCardDeck
#import "PlayingCardDeck.h"
#import "PlayingCard.h"
#implementation PlayingCardDeck
- (id)init
{
self = [super init];
if(self) {
for(NSString *suit in [PlayingCard validSuits]){
for (NSUInteger rank = 1; rank <= [PlayingCard maxRank]; rank++){
PlayingCard *card = [[PlayingCard alloc] init];
card.rank = rank;
card.suit = suit;
[PlayingCardDeck addCard:card atTop:NO];
}
}
}
return self;
}
#end
Class: Deck
#import "Deck.h"
#interface Deck()
#property (strong, nonatomic) NSMutableArray *cards;
#end
#implementation Deck
- (NSMutableArray *)cards
{
if(!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void) addCard:(Card *)card atTop:(BOOL)atTop
{
if(atTop){
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
- (Card *)drawRandomCard
{
Card *randomCard = nil;
if(self.cards.count){
unsigned index = arc4random() % self.cards.count;
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
#end
thank you all for the help.
The method -addCard:atTop: is an instance method. It must be sent to an instance of a class, not the class itself. This line:
[PlayingCardDeck addCard:card atTop:NO];
is attempting to send it to the class PlayingCardDeck, not any instance of that class. That's what the error is trying to tell you.
Since that code is in the -init method of the PlayingCardDeck class, you probably meant to send the message to self:
[self addCard:card atTop:NO];

Object loses reference

I have a IKImageBrowserView which has its own controller - BrowserController.h + .m
#interface BrowserController : NSWindowController{
NSMutableArray *_images;
}
#property (strong,nonatomic) IBOutlet IKImageBrowserView *imageBrowser;
-(void)addMultipleImages;
When I run the app for the first time, the staring image loads, but when I click a button to add other images and call a method from another class I don't get any results. I have noticed that my _imageBrowser loses the reference and becomes nil.
How could I solve this issue?
AppDelegate.m
#import "AppDelegate.h"
#import "BrowserController.h"
#implementation AppDelegate{
BrowserController *imageBrowserController;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
imageBrowserController = [BrowserController sharedManager];
}
- (IBAction)doSomething:(id)sender {
[imageBrowserController addMultipleImages];
}
#end
BrowserController.m
#import "BrowserController.h"
#interface myImageObject : NSObject
{
NSString *_path;
}
#end
#implementation myImageObject
/* our datasource object is just a filepath representation */
- (void)setPath:(NSString *)path
{
if(_path != path)
{
_path = path;
}
}
/* required methods of the IKImageBrowserItem protocol */
#pragma mark -
#pragma mark item data source protocol
/* let the image browser knows we use a path representation */
- (NSString *)imageRepresentationType
{
return IKImageBrowserPathRepresentationType;
}
/* give our representation to the image browser */
- (id)imageRepresentation
{
return _path;
}
/* use the absolute filepath as identifier */
- (NSString *)imageUID
{
return _path;
}
#end
#interface BrowserController ()
#end
#implementation BrowserController
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void)awakeFromNib{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
- (void)updateDataSource{
[_imageBrowser reloadData];
}
-(NSUInteger) numberOfItemsInImageBrowser:(IKImageBrowserView *)aBrowser{
return [_images count];
};
-(id)imageBrowser:(IKImageBrowserView *)aBrowser itemAtIndex:(NSUInteger)index{
return [_images objectAtIndex:index];
};
- (void)updateDatasource
{
[_imageBrowser reloadData];
}
- (void)addMultipleImages{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[_images addObject:p];
[_images addObject:p];
[_imageBrowser reloadData];
}
+ (id)sharedManager {
static BrowserController *sharedMyManager = nil;
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
#end
There is some confusion as to the name of the class where you mention it's called ImageBrowserController at the start of your question and it looks like it's called BrowserController at the end of your question.
The issue is that you allocate _images in awakeFromNib which is never called given the class is created via the singleton pattern (sharedInstance) and not loaded from a .nib file.
Therefore move the code from awakeFromNib into init and dump awakeFromNib:
BrowserController.m:
- (id)init
{
self = [super init];
if (self) {
myImageObject *p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
return self;
}
Further confusion: you have implemented an initWithFrame method. Why?

Why can't I populate my controller with items?

I'm using an ItemController to provide a list of items to use in a tableview. I can't seem to populate the controller though, and I'm not sure why.
Here's the code for the controller class:
.h
#import <Foundation/Foundation.h>
#class Item;
#interface ItemController : NSObject
#property (nonatomic, copy) NSMutableArray *items;
- (NSUInteger)countOfList;
- (Item*)objectInListAtIndex:(NSUInteger)theIndex;
- (void)addItem:(Item *)item;
#end
.m
#import "ItemController.h"
#import "Item.h"
#interface ItemController ()
#end
#implementation ItemController
- (NSUInteger)countOfList {
return [self.items count];
}
- (Item *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.items objectAtIndex:theIndex];
}
- (void)addItem:(Item *)item {
[self.items addObject:item];
}
#end
Item.m
#implementation Item
-(id)initWithName:(NSString *)name{
self = [super init];
if (self) {
_name = name;
return self;
}
return nil;
}
#end
I'm using the following code to populate the list:
ItemController* controller = [[ItemController alloc] init];
for (NSString* key in raw_data) {
NSLog(key); // This outputs the keys fine
[controller addItem:[[Item alloc] initWithName:key]];
}
NSLog([NSString stringWithFormat:#"%d",[controller countOfList]]); // Always 0
You need to initialize the array in the init methond.
- (id)init {
self = [super init];
if (self) {
self.items = [[NSMutableArray alloc] init];
}
return self;
}
You need to initialize your variable items. In your init method, call self.items = [NSMutableArray new]; and also change your array property from copy to retain.
I also believe your class ItemController should be of kind UIViewController and not NSObject.
#interface ItemController : UIViewController
You don't initialise the _items instance variable anywhere, so it's always nil. The result of any integer-returning method called on nil will be 0, so you see that the count is 0.

NSKeyedArchiver and description method : app crashes

i tried to test NSKeyedArchiver, but my code seems to be wrong. I then tried only with a NSLog, but the NSLog returns (null) if i alloc-init my var "model" in the init method (in the controller), and it makes the app crash if i put it in viewDidLoad.
Can someone have a look and tell me if something is wrong?
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#class Model;
#interface AAViewController : UIViewController {
Model *model;
}
#property (nonatomic, retain) Model *model;
//+(BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
#end
______
#import "AAViewController.h"
#import "Model.h"
#implementation AAViewController
#synthesize model;
- (id) init {
self = [super init];
if (self) {
model = [[Model alloc] initWithName:#"thomas" age:13];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
//self.model = [[Model alloc] initWithName:#"thomas" age:13];
NSLog (#"%#", self.model);
/*
if (![NSKeyedArchiver archiveRootObject:model toFile:#"test.archive"]){
NSLog (#"erreur");
[model release];
}
*/
NSLog(#"success !");
}
- (void)viewDidUnload {
//model = nil;
}
- (void)dealloc {
[model release];
[super dealloc];
}
#end
______
#import <Foundation/Foundation.h>
//#interface Model : NSObject <NSCoding>
#interface Model : NSObject {
NSString *name;
int age;
}
#property (nonatomic, copy) NSString *name;
#property (nonatomic) int age;
- (id) initWithName:(NSString *)theName age:(int)theAge;
/*
- (id) initWithCoder:(NSCoder *)encoder;
- (void) encodeWithCoder:(NSCoder *)decoder;
*/
#end
______
#import "Model.h"
#implementation Model
#synthesize name, age;
- (id) initWithName:(NSString *)theName age:(int)theAge {
self = [super init];
if (self) {
name = [theName copy];
age = theAge;
}
return self;
}
- (void) dealloc {
[name release];
[super dealloc];
}
/*
- (id) initWithCoder:(NSCoder *)decoder{
self = [super init];
if (self) {
name = [[decoder decodeObjectForKey:#"nom"] retain];
age = [decoder decodeIntForKey:#"age"];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:name forKey:#"nom"];
[encoder encodeInt:age forKey:#"age"];
}
*/
- (NSString *) description {
return [NSString stringWithFormat:#"the name is : %#, %# years old", name, age];
}
#end
Your app is crashing because this line is wrong:
return [NSString stringWithFormat:#"the name is : %#, %# years old", name, age];
age is an int, not an object pointer. Needs to be:
return [NSString stringWithFormat:#"the name is : %#, %d years old", name, age];
I don't think there's anything obviously wrong with your encode/decode code.

Objective C - Singleton class instane not initialising

I have declared a class called 'SharedTranslationsArray' that I want to use in multiple view controllers.
I then in the view controller MainViewController.m I declare an instance of the variable in the "ViewDidLoadMethod" and try to add an object to the array in the singleton instance. There are no compilation errors / warning but the items are not added to the array.
Any advice would be appreciated. The relevant code samples are below
Thanks
**SharedTranslations.h**
#import "Foundation/Foundation.h"
#interface SharedTranslationsArray : NSObject {
NSMutableArray *translation_set;
}
static SharedTranslationsArray *sharedInstance;
#property (nonatomic, retain) NSMutableArray *translation_set;
+ (SharedTranslationsArray*) sharedInstance;
#end
**SharedTranslations.m**
#import "SharedTranslationsArray.h"
static SharedTranslationsArray *sharedInstance;
#implementation SharedTranslationsArray
#synthesize translation_set;
\+ (SharedTranslationsArray*)sharedInstance
{
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
\ + (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance]retain];
}
\ - (id)copyWithZone:(NSZone *)zone
{
return self;
}
\ - (id)retain
{
return self;
}
\ - (unsigned)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
\ - (void)release
{
//do nothing
}
\ - (id)autorelease
{
return self;
}
#end
**MainViewController.m**
#import "MainViewController.h"
#import "Translations.h"
#import "SharedTranslationsArray.h"
#implementation MainViewController
\- (void)viewDidLoad {
NSMutableString *temp = [[NSMutableString alloc] init];
SharedTranslationsArray *ts = [SharedTranslationsArray sharedInstance];
Translations *translation = [Translations new];
translation.shortText = #"short";
translation.fullText = #"long";
translation.canDeleted = FALSE;
translation.active = TRUE;
[ts.translation_set addObject:translation];
}
#end
Make sure you actually initialize translation_set!
+ (SharedTranslationsArray*)sharedInstance
{
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
sharedInstance.translation_set = [NSMutableArray array];
}
return sharedInstance;
}
Also why are you calling it a set but declaring it as an array? If the order of the objects is not important you could make it an NSMutableSet