NSKeyedArchiver and description method : app crashes - objective-c

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.

Related

Class Not Running

I'm new to Xcode and Objective C, but learning fast. I am writing a Bluetooth LE app to collect data from multiple BLE devices. Happy with CoreBluetooth and am able to get what I want to function and collect the data.
However, I did it all within AppDelegate and now want to separate out different sections of code into neat Classes.
Code compiles okay but nothing runs other than AppDelegate.
Example of the class - SensorDev.m:
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
#class SensorDev;
#protocol SensorDevDelegate<NSObject>
- (void) sensorDevDidChangeStatus:(SensorDev*)dev;
#end
#interface SensorDev : NSObject
#property (nonatomic, assign, readonly) id<SensorDevDelegate> delegate;
#property (nonatomic, readonly) CBPeripheral *peripheral;
- (id)initWithPeripheral:(CBPeripheral *)peripheral controller:(id<SensorDevDelegate>)controller;
- (void)start;
#end
Example of the class - SensorDev.h
#import "SensorDev.h"
NSString *SR1Device9DOFServiceUUIDString = #"346D0000";
NSString *SR1Device9DOFCharacteristicUUIDString = #"346D0001-12A9-11CF-1279-81F2B7A91332";
#interface SensorDev() <CBPeripheralDelegate> {
CBService *_temperatureService;
CBCharacteristic *_temperatureCharacteristic;
}
#end
#implementation SensorDev
#pragma mark - Setup
- (id)initWithPeripheral:(CBPeripheral *)peripheral controller:(id<SensorDevDelegate>)controller
{
self = [super init];
if (self) {
_peripheral = peripheral;
_peripheral.delegate = self;
_delegate = controller;
}
return self;
}
#pragma mark - Start
// -------------------------------------------------------------------------------
// Startup
// -------------------------------------------------------------------------------
- (void)start
{
NSLog(#"- (void) start"); //--Debug
CBUUID *serviceUUID = [CBUUID UUIDWithString:SR1Device9DOFServiceUUIDString];
NSArray *serviceArray = [NSArray arrayWithObjects:serviceUUID, nil];
[_peripheral discoverServices:serviceArray];
}
#end
I don't get the debug line in the log:
NSLog(#"- (void) start"); //--Debug
Looking for help guys....what am I missing ...thanks in advance ....
UPDATE
So I have a second class that does all the CoreBluetooth setup and discovery
Discovery.h
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import "SensorDev.h"
// -------------------------------------------------------------------------------
//UI Setup/Protocols
// -------------------------------------------------------------------------------
#protocol DiscoveryDelegate <NSObject>
- (void) discoveryDidRefresh;
- (void) discoveryStatePoweredOff;
#end
#interface Discovery : NSObject
+(Discovery*) sharedInstance;
#property (nonatomic, assign) id<DiscoveryDelegate> discoveryDelegate;
#property (nonatomic, assign) id<SensorTagDelegate> peripheralDelegate;
// -------------------------------------------------------------------------------
// Actions
// -------------------------------------------------------------------------------
- (void) startScanningForUUIDString:(NSString *)uuidString;
- (void) stopScanning;
- (void) connectPeripheral:(CBPeripheral*)peripheral;
- (void) disconnectPeripheral:(CBPeripheral*)peripheral;
// -------------------------------------------------------------------------------
// Access to the devices
// -------------------------------------------------------------------------------
#property (readonly, nonatomic) NSMutableArray *foundPeripherals;
#property (retain, nonatomic) NSMutableArray *connectedPeripherals;
#end
Discover.m (extract)
#import "Discovery.h"
extern NSString *SR1Device9DOFServiceUUIDString; //346D0000
extern NSString *SR1Device9DOFCharacteristicUUIDString; //346D0001-12A9-11CF-1279-81F2B7A91332
#interface Discovery() <CBCentralManagerDelegate, CBPeripheralDelegate> {
CBCentralManager *_centralManager;
BOOL _pendingInit;
}
#end
#implementation Discovery
#pragma mark - Setup
+ (Discovery*) sharedInstance
{
static Discovery *this = nil;
if (!this)
this = [[Discovery alloc] init];
return this;
}
- (id) init
{
self = [super init];
if (self) {
_pendingInit = YES;
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
_foundPeripherals = [[NSMutableArray alloc] init];
_connectedPeripherals = [[NSMutableArray alloc] init];
}
return self;
}
#pragma mark - CoreBluetooth Services
// -------------------------------------------------------------------------------
// CoreBluetooth Start/Stop Scanning
// -------------------------------------------------------------------------------
- (void)startScanningForUUIDString:(NSString *)uuidString
{
NSLog(#"- (void) startScanningForUUIDString"); //--Debug
[_centralManager scanForPeripheralsWithServices:
[NSArray arrayWithObjects:SR1Device9DOFServiceUUIDString, nil] options:nil];
}
- (void)stopScanning
{
NSLog(#"- (void) stopScanning"); //--Debug
}
// -------------------------------------------------------------------------------
// CoreBluetooth Connect/Disconnect
// -------------------------------------------------------------------------------
- (void) connectPeripheral:(CBPeripheral*)peripheral
{
NSLog(#"- (void) connectPeripheral"); //--Debug
if (peripheral.state == CBPeripheralStateDisconnected) {
[_centralManager connectPeripheral:peripheral options:nil];
}
}
- (void) disconnectPeripheral:(CBPeripheral*)peripheral
{
NSLog(#"- (void) disconnectPeripheral"); //--Debug
[_centralManager cancelPeripheralConnection:peripheral];
}
- (void) centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(#"- (void) didConnectPeripheral"); //--Debug
SensorDev *tag = nil;
// Create a service instance.
tag = [[SensorDev alloc] initWithPeripheral:peripheral controller:_peripheralDelegate];
[tag start];
if (![_connectedPeripherals containsObject:tag])
[_connectedPeripherals addObject:tag];
[_peripheralDelegate sensorTagDidChangeStatus:tag];
[_discoveryDelegate discoveryDidRefresh];
}
This isn't running either ...
The reason why the classes were not running as there was nothing calling them. The class associated to the view controller was not calling the class:
BLEController.h
#import <Foundation/Foundation.h>
#interface BLEController : NSObject {
enter code here
NSMutableArray *_periphralItems;
}
#end
BLEController.m
#import "SensorDev.h"
#import "Discovery.h"
#import "BLEController.h"
#interface BLEController() <DiscoveryDelegate, SensorTagDelegate>
{
BOOL _selfSelection;
}
#end
#implementation BLEController
- (void)awakeFromNib
{
[[Discovery sharedInstance] setDiscoveryDelegate:self];
[[Discovery sharedInstance] setPeripheralDelegate:self];
_periphralItems = [NSMutableArray new];
}
- (void) discoveryDidRefresh
{
}
- (void) discoveryStatePoweredOff
{
}
- (void)sensorTagDidChangeStatus:(SensorTag *)tag
{
if(tag.peripheral.state == CBPeripheralStateConnected) {
//Do something
}
}
#end
The awakeFromNib allows me to call the other classes....

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

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.

NSUserDefaults for self created classes

I'm having a problem in a usually very simple quest, I want to create a custom class containing an integer, a NSString and a NSDate. However, in my example I only use a NSString to show my problem when storing objects.
This is my class header:
#import
#interface MyClass : NSObject <NSCoding> {
NSString * myString;
}
#property (retain) NSString * myString;
#end
and my body:
#import "MyClass.h"
#implementation MyClass
#synthesize myString;
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
myString = [coder decodeObjectForKey:#"myString"];
}
return self;
}
- (id)init {
self = [super init];
if (self) {
myString = #"Hi";
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject: myString forKey:#"myString"];
}
- (void)dealloc {
myString = nil;
[super dealloc];
}
#end
My ViewController contains two buttons called Create and Read, as well as a UILabel called label.
#import <UIKit/UIKit.h>
#interface UserDefaultsTestViewController : UIViewController {
IBOutlet UILabel *label;
}
- (IBAction)pushCreate;
- (IBAction)pushLoad;
#end
I will only show the functions I've edited here:
#import "UserDefaultsTestViewController.h"
#import "MyClass.h"
- (IBAction)pushCreate {
MyClass * myClass = [[MyClass alloc] init];
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:myClass];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:data forKey:#"Test"];
[myClass release];
}
- (IBAction)pushLoad {
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSData * atchive = [defaults objectForKey:#"Test"];
MyClass * myClass = [NSKeyedUnarchiver unarchiveObjectWithData:atchive];
label.text = myClass.myString;
}
However, if I run my code, I get either a EXC_BAD_ACCESS error or it displays it correctly the first time and crashes when doing it a second time, again with the same error.
What am I doing wrong?
You want to do:
self.myString = #"Hi";
Or:
myString = #"Hi";
[myString retain];
Likewise, your dealloc should have:
self.myString = nil;
or
[myString release];

NSMutableArray KVC/KVO question

This is a sample from book "Cocoa Programming For Mac Os X 3rd(HD)" chapter 7 "Key-Value Coding. Key-Vaule Observing
Here is the code:
Person.h:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
NSString *personName;
float expectedRaise;
}
#property (readwrite, copy) NSString *personName;
#property (readwrite) float expectedRaise;
#end
Person.mm:
#import "Person.h"
#implementation Person
#synthesize expectedRaise;
#synthesize personName;
- (id)init
{
[super init];
expectedRaise = 0.05;
personName = #"New Person";
return self;
}
- (void)dealloc
{
[personName release];
[super dealloc];
}
#end
MyDocument.h:
#import <Cocoa/Cocoa.h>
#interface MyDocument : NSDocument
{
NSMutableArray *employees;
}
#property (retain) NSMutableArray *employees;
#end
MyDocument.mm:
#import "MyDocument.h"
#import "Person.h"
#implementation MyDocument
#synthesize employees;
- (id)init
{
if (![super init])
return nil;
employees = [[NSMutableArray alloc] init];
return self;
}
- (void)dealloc
{
[employees release];
[super dealloc];
}
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
#end
And the sample works fine.(A blank table at first and you can add or delete record).
Now I tried to add some records to the array so that the blank table would have
someting in it at first.
Here's what I'v tried(inside the init method):
[self willChangeValueForKey:#"employees"];
Person *p1 = [[Person alloc] init];
[employees addObject: [NSData dataWithBytes: &p1 length: sizeof(p1)]];
[self didChangeValueForKey:#"employees"];
But when I build and wrong I got the error msg:
[<NSConcreteData 0x422bf0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key personName.
......
Can any one help me out of here? Thanks in advance ^_^
That seems like a very reasonable response... you added NSData to your array named employees; guessing from the names, and from the KVC, you probably meant to add p1 to your array instead. So, try:
[employees addObject:p1];

Why "copy" is not being invoked?

I have the following object:
#interface SomeObject : NSObject
{
NSString *title;
}
#property (copy) NSString *title;
#end
And I have another object:
#interface AnotherObject : NSObject{
NSString *title;
}
#property (copy) NSString *title;
- (AnotherObject*)init;
- (void)dealloc;
- (void) initWithSomeObject: (SomeObject*) pSomeObject;
+ (AnotherObject*) AnotherObjectWithSomeObject (SomeObject*) pSomeObject;
#end
#implementation AnotherObject
#synthesize title
- (AnotherObject*)init {
if (self = [super init]) {
title = nil;
}
return self;
}
- (void)dealloc {
if (title) [title release];
[super dealloc];
}
-(void) initWithSomeObject: (SomeObject*) pSomeObject
{
title = [pSomeObject title]; //Here copy is not being invoked, have to use [ [pSomeObject title] copy]
}
+ (AnotherObject*) AnotherObjectWithSomeObject (SomeObject*) pSomeObject;
{
[pSomeObject retain];
AnotherObject *tempAnotherObject = [ [AnotherObject alloc] init];
[tempAnotherObject initWithSomeObject: pSomeObject];
[pSomeObject release];
return tempAnotherObject;
}
#end
I do not understand, why copy is not being invoked when I am assigning "title = [pSomeObject title]". I always thought if i set "copy" in property it is always going to be invoked. I have a error in my code or I don't understand something?
Thank you in advance.
For a setter to get called you need to use the dot notation.
self.title = [pSomeObject title];
or...to use the dot notation for pSomeObject too
self.title = pSomeObject.title;