I am a beginner with Xcode and Objective-C and stuck with a quite simple thing for over two days now. I hope you can help me.
My Project is deployed for OS X 10.6, it uses Garbage Collection and I am using Xcode 4.0.1.
I made a multi document application starting with the template provided by Xcode. I just have one class as subclass of NSDocument.
For opening documents I use initWithCoder:. The decoding within this method works fine - I get the values that were saved.
But these values are "lost" when I would like to use them in an other method (of the same class).
I assume that I make some mistakes with using the right comibation of init: initWithCoder:, initWithContentsOfURL: etc.
The self-object does always have a different adress in the initWithCoder: method then in the other methods.
I tried plenty of combinations of the above methods and even tried to call different methods in the super class (NSDocument) within initWithCoder:.
This is my header file:
#import <Cocoa/Cocoa.h>
#interface OptimiererZwoMultiDoc : NSDocument <NSCoding> {
__strong struct bildanalyse {
float winkelo;
...
float skalfak; // Der Skalierungsfaktor, den dieses Bild erfahren muss damit es so gross ist wie das kleinste - Wert ist also immer <= 0
};
__strong struct bildanalyse *analyse;
__strong int16_t anzahlanalysewerte;
...
#private
NSTextView *ausgabe;
...
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
- (void) prepareAnalyseDoc;
...
#property (assign) IBOutlet NSTextView *ausgabe;
#property __strong struct bildanalyse *analyse;
#property __strong int16_t anzahlanalysewerte;
#end
When I try this implementation:
#import "OptimiererZwoMultiDoc.h"
#implementation OptimiererZwoMultiDoc
#synthesize ausgabe;
#synthesize analyse;
#synthesize anzahlanalysewerte;
...
- (id)init
{
self = [super init];
NSLog(#"init self=%#",self);
if (self) {
...
}
return self;
}
- (NSString *)windowNibName
{
NSLog(#"windowNibName self=%#",self);
return #"OptimiererZwoMultiDoc";
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
NSLog(#"windowControllerDidLoadNib self=%#",self);
[super windowControllerDidLoadNib:aController];
}
- (BOOL) readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError{
NSLog(#"readFromData self=%#",self);
[NSKeyedUnarchiver unarchiveObjectWithData: data];
if (outError) {
*outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:NULL];
}
return YES;
}
- (id) initWithCoder: (NSCoder *) coder{
struct bildanalyse tempAnalyse;
NSLog(#"initWithCoder self=%#",self);
anzahlanalysewerte = [coder decodeIntForKey:#"anzahlanalysewerte"];
....
return self;
}
then I get this output:
init self= OptimiererZwoMultiDoc: 0x2002955a0
readFromData self= OptimiererZwoMultiDoc: 0x2002955a0
initWithCoder self= OptimiererZwoMultiDoc: 0x20028f5e0
windowNibName self= OptimiererZwoMultiDoc: 0x2002955a0
windowControllerDidLoadNib self= OptimiererZwoMultiDoc: 0x2002955a0
As you can see, the object self is different in initWithCoder:. Why? What's wrong with my code?
Your -initWithCoder: is missing a self = [super initWithCoder:coder];. I’m not sure why your -init and -initWithCoder: are both being called—an object that’s getting unarchived from a NIB should just be receiving the latter—but the above would be a good place to start.
It's because (provided the document info in your Info.plist is set up correctly) Cocoa's document-handling machinery automatically instantiates your NSDocument subclass for you. So if you're using -initWithCoder: to manually create your own subclass instance, chances are that one is completely superfluous. I'd suggest reading through Apple's Document-Based Applications Overview before going much further.
Related
I am trying to subclass NSNotification.
Apple's docs for NSNotificationstate the following:
NSNotification is a class cluster with no instance variables. As such,
you must subclass NSNotification and override the primitive methods
name, object, and userInfo. You can choose any designated initializer
you like, but be sure that your initializer does not call
NSNotification’s implementation of init (via [super init]).
NSNotification is not meant to be instantiated directly, and its init
method raises an exception.
But this isn't clear to me. Should I create an initializer like this?
-(id)initWithObject:(id)object
{
return self;
}
Subclassing NSNotification is an atypical operation. I think I've only seen it done once or twice in the past few years.
If you're looking to pass things along with the notification, that's what the userInfo property is for. If you don't like accessing things through the userInfo directly, you could use a category to simplify access:
#interface NSNotification (EasyAccess)
#property (nonatomic, readonly) NSString *foo;
#property (nonatomic, readonly) NSNumber *bar;
#end
#implementation NSNotification (EasyAccess)
- (NSString *)foo {
return [[self userInfo] objectForKey:#"foo"];
}
- (NSNumber *)bar {
return [[self userInfo] objectForKey:#"bar"];
}
#end
You can also use this approach to simplify NSNotification creation. For example, your category could also include:
+ (id)myNotificationWithFoo:(NSString *)foo bar:(NSString *)bar object:(id)object {
NSDictionary *d = [NSDictionary dictionaryWithObjectsForKeys:foo, #"foo", bar, #"bar", nil];
return [self notificationWithName:#"MyNotification" object:object userInfo:d];
}
If, for some strange reason, you'd need the properties to be mutable, then you'd need to use associative references to accomplish that:
#import <objc/runtime.h>
static const char FooKey;
static const char BarKey;
...
- (NSString *)foo {
return (NSString *)objc_getAssociatedObject(self, &FooKey);
}
- (void)setFoo:(NSString *)foo {
objc_setAssociatedObject(self, &FooKey, foo, OBJC_ASSOCIATION_RETAIN);
}
- (NSNumber *)bar {
return (NSNumber *)objc_getAssociatedObject(self, &BarKey);
}
- (void)setBar:(NSNumber *)bar {
objc_setAssociatedObject(self, &BarKey, bar, OBJC_ASSOCIATION_RETAIN);
}
...
It seems this does work. For example:
#import "TestNotification.h"
NSString *const TEST_NOTIFICATION_NAME = #"TestNotification";
#implementation TestNotification
-(id)initWithObject:(id)object
{
object_ = object;
return self;
}
-(NSString *)name
{
return TEST_NOTIFICATION_NAME;
}
-(id)object
{
return object_;
}
- (NSDictionary *)userInfo
{
return nil;
}
#end
also beware a massive Gotcha related to NSNotifications. The type of NSNotifications greated using NSNotification notificationWithName:object: is NSConcreteNotification, not NSNotification. And to make it a little more awkward, if you are checking for class, NSConcreteNotification is private so you have nothing to compare to.
You don’t set it, exactly—you just override the implementation of the name method so it returns what you want. In other words:
- (NSString *)name
{
return #"Something";
}
Your initializer looks fine—I haven’t seen an example of an init that doesn’t call its superclass’s implementation before, but if that’s what the doc’s saying you should do, it’s probably worth a try.
You can pass a userInfo argument when delivering a notification. Why not create a payload and send that.
// New file:
#interface NotificationPayload : NSObject
#property (copy, nonatomic) NSString *thing;
#end
#implementation NotificationPayload
#end
// Somewhere posting:
NotificationPayload *obj = [NotificationPayload new];
obj.thing = #"LOL";
[[NSNotificationCenter defaultCenter] postNotificationName:#"Hi" object:whatever userInfo:#{ #"payload": obj }];
// In some observer:
- (void)somethingHappened:(NSNotification *)notification
{
NotificationPayload *obj = notification.userInfo[#"payload"];
NSLog(#"%#", obj.thing);
}
Done.
As a side note: I've found over the years that making a conscious effort to avoid subclassing has made my code more clean, maintainable, changeable, testable and extensible. If you can solve the problem using protocols or categories then you wont lock yourself into the first shoddy design you come up with. With Swift 2.0 protocol extensions in the mix we're really laughing too.
I am trying to set the value of an NSTextField, but it's not working properly.
I have a button linked to an IBAction, and when I set it using self, it works fine:
#import <Foundation/Foundation.h>
#interface TestMessage : NSObject {
IBOutlet NSTextField *text;
}
- (IBAction) setMessage: (id) controller;
- (void) Message:(NSString *) myMessage;
#end
#import "TestMessage.h"
#implementation TestMessage
- (IBAction) setMessage: (id) controller {
// This works
[self Message:#"Hello"];
// but this doesn't
TestMessage * messageTest= [TestMessage new];
[messageTest Message:#"Hi"];
}
- (void) Message: (NSString *) myMessage {
[text setStringValue: myMessage];
NSLog(#"Message Was Called");
// This returns <NSTextField: 0x1001355b0> when called
// using self, but null when called the other way.
NSLog(#"%#", text);
}
#end
I've searched for a while, but still can't find the answer.
I guess it has something to do with the delegate, but I'm not sure.
Thanks in advance.
Are you sure message is called when you call it from anotherFuntion? If anotherFuntion is a method of another class, calling [self message:] won't work as you expected to...
I know this is an old post, but I have been fiddling with the same issue today.
You have to return string value in textfield:
[textField stringValue];
The code
TestMessage * messageTest = [TestMessage new];
is unusual, specifically new. I'm going to assume that new is just a class method does normal alloc/init equivalent to
TestMessage * messageTest = [[TestMessage alloc] init];
The main problem is that IBOutlet NSTextField *text will be initialized only if the class TestMessage is loaded with a Nib file. It would have to be named as the class of an object in Interface Builder, like so
and you would have to implement initWithCoder and encodeWithCoder something like this in order to extract your field value from the IB encoding:
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.text = [coder decodeObjectForKey:#"text"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeObject:self.text forKey:#"text"];
}
Fundamentally, IBOutlet fields do not get wired up wherever you create an instance of that class. If they did, how would you express that field A should be wired to UI object A and field B should be wired to UI object B? The connection is established only in the context of loading a class from a Nib file.
Simple question: where do the tableView and section arguments get passed from? The actual code in the method return [self.listData count]; doesn't even mention them.
Here's my interface code:
#interface Simple_TableViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource>
{
NSArray *listData;
}
#property (nonatomic, retain) NSArray *listData;
#end
And this is all the implementation code:
#import "Simple_TableViewController.h"
#implementation Simple_TableViewController
#synthesize listData;
- (void)viewDidLoad {
NSArray *array = [[NSArray alloc] initWithObjects:#"Sleepy", #"Sneezy",
#"Bashful", #"Happy", #"Doc", #"Grumpy", #"Dopey", #"Thorin",
#"Dorin", #"Nori", #"Ori", #"Balin", #"Dwalin", #"Fili", #"Kili",
#"Oin", #"Gloin", #"Bifur", #"Bofur", #"Bombur", nil];
self.listData = array;
[array release];
[super viewDidLoad];
}
- (void)viewDidUnload {
self.listData = nil;
}
- (void)dealloc {
[listData release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.listData count];
}
I just want to know how does the method (NSInteger)tableView: (UITableView *)numberOfRowsInSection: receive those arguments? Of course this happens everywhere; I just want to understand it.
The Simple_TableViewController class is likely meant to manage a single table with a single section. Given that, the tableView and section parameters aren't important because they can only be one thing: a pointer to the table and 0, respectively.
Your view controller class is adding support for these callback methods through UITableViewDelegate and UITableViewDataSource. You are adding this support in your .h file through <UITableViewDelegate, UITableViewDataSource>. These classes are built in to the Cocoa Touch framework and you are just using them. When the table is (re)loaded, this callback methods are called if you have defined them (some are required, others are optional).
I have a Class that runs the following method (a getter):
// the interface
#interface MyClass : NSObject{
NSNumber *myFloatValue;
}
- (double)myFloatValue;
- (void)setMyFloatValue:(float)floatInput;
#end
// the implementation
#implementation
- (MyClass *)init{
if (self = [super init]){
myFloatValue = [[NSNumber alloc] initWithFloat:3.14];
}
return self;
}
// I understand that NSNumbers are non-mutable objects and can't be
// used like variables.
// Hence I decided to make make the getter's implementation like this
- (double)myFloatValue{
return [myFloatValue floatValue];
}
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput;
}
#end
When I mouse over the myFloatValue object during debugging, it does not contain a value. Instead it says: "out of scope".
I would like to be able to make this work without using #property, using something other than NSNumbers, or other major changes since I just want to understand the concepts first. Most importantly, I would like to know what mistake I've apparently made.
I can see a couple of mistakes:
The line #implementation should read #implementation MyClass
The function setMyFloatValue is missing a closing ] and } —it should read:
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput];
}
}
I've just tested it in Xcode and it works for me with these changes.
Why not just set property in interface and synthesize accessors in implementation?
#interface MyClass : NSObject {
float *myFloat
}
#property (assign) float myFloat;
#end
#implementation MyClass
#synthesize myFloat;
#end
If I have a custom class called Tires:
#import <Foundation/Foundation.h>
#interface Tires : NSObject {
#private
NSString *brand;
int size;
}
#property (nonatomic,copy) NSString *brand;
#property int size;
- (id)init;
- (void)dealloc;
#end
=============================================
#import "Tires.h"
#implementation Tires
#synthesize brand, size;
- (id)init {
if (self = [super init]) {
[self setBrand:[[NSString alloc] initWithString:#""]];
[self setSize:0];
}
return self;
}
- (void)dealloc {
[super dealloc];
[brand release];
}
#end
And I synthesize a setter and getter in my View Controller:
#import <UIKit/UIKit.h>
#import "Tires.h"
#interface testViewController : UIViewController {
Tires *frontLeft, *frontRight, *backleft, *backRight;
}
#property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;
#end
====================================
#import "testViewController.h"
#implementation testViewController
#synthesize frontLeft, frontRight, backleft, backRight;
- (void)viewDidLoad {
[super viewDidLoad];
[self setFrontLeft:[[Tires alloc] init]];
}
- (void)dealloc {
[super dealloc];
}
#end
It dies after [self setFrontLeft:[[Tires alloc] init]] comes back. It compiles just fine and when I run the debugger it actually gets all the way through the init method on Tires, but once it comes back it just dies and the view never appears. However if I change the viewDidLoad method to:
- (void)viewDidLoad {
[super viewDidLoad];
frontLeft = [[Tires alloc] init];
}
It works just fine. I could just ditch the setter and access the frontLeft variable directly, but I was under the impression I should use setters and getters as much as possible and logically it seems like the setFrontLeft method should work.
This brings up an additional question that my coworkers keep asking in these regards (we are all new to Objective-C); why use a setter and getter at all if you are in the same class as those setters and getters.
You have declared frontLeft as a 'copy' property:
#property (nonatomic,copy) Tires *frontLeft, *frontRight, *backleft, *backRight;
When you assign to this property, a copy is made by invoking the object's copy method. This only works for objects which support the NSCopying protocol (i.e., which implement a copyWithZone: method). Since your Tires class does not implement this method, you get an exception.
You probably want to change this to be a 'retain' property:
#property (nonatomic,retain) Tires *frontLeft, *frontRight, *backleft, *backRight;
See the Objective C documentation on declared properties for more on property declarations.
One problem that i see is here:
- (void)viewDidLoad {
[super viewDidLoad];
[self setFrontLeft:[[Tires alloc] init]];
}
When you call [Tires alloc] you get back an object with a retain count of 1. You then use a set method which you have synthesized, which bumps the retain count to 2. When your object is done with the Tire object, it will reduce the retain count back to 1, but the tire will never get deallocated. I think you should use:
[self setFrontLeft:[[[Tires alloc] init] autorelease]];