Why is this pointer type incompatible - iphone

This is the code
Dest.h
#import <UIKit/UIKit.h>
#import <CoreGraphics/CGPDFArray.h>
#class Model;
// snip
#interface Dest : NSObject
{
CGPDFArrayRef destArray;
DestKind kind;
}
+ (id)destWithObject:(CGPDFObjectRef)obj inModel:(Model*)model;
- (id)initWithArray:(CGPDFArrayRef)array;
Dest.m
#implementation Dest
+ (id)destWithObject:(CGPDFObjectRef)obj inModel:(PDFModel*)model
{
CGPDFArrayRef array = NULL;
Dest* dest = nil;
// stuff to create array
if (array)
{
dest = [[[Dest alloc] initWithArray:array] autorelease];
<path>/Dest.m:63: warning: passing argument 1 of 'initWithArray:' from incompatible pointer type
}
return dest;
}
Clearly the compiler thinks that array is incompatible with initWithArray: declared in Dest.h. But as far as I can see, the type is exactly right. I even copied the declaration from Dest.h and pasted it in Dest.m. initWithArray: compiles fine. Adding/removing the CGPDFArray.h header file in Dest.h doesn't make any difference, the compiler doesn't think it is an int in Dest.h.

I have a feeling you're leaving out another warning that's relevant — "warning: multiple methods named 'initWithArray:' found". If I'm right, this is what you're running into:
There are two method signatures that go with that selector. NSArray's takes an NSArray* and yours takes a CGPDFArrayRef.
alloc returns id. This means that the compiler has no idea what class it returns (yes, the compiler is that thick).
You then send initWithArray: to this mystery object. The compiler says, "Gosh, I don't know what kind of object this is, so I can't decide which method signature is correct. I'll spin around really fast and whichever one I'm facing is the one I'll pick." It chooses NSArray's signature. Then it looks at the argument and says, "Hey, that's not an NSArray! Error!"
The quick-and-easy solution is to change it to [[(Dest*)[Dest alloc] initWithArray:array] autorelease]. The better solution is to choose a distinct selector for your method.

Oh don't do that. Only CFArrayRefs are 'toll-free bridged' to NSArray. The CGPDFArrayRef however is completely different and incompatible. You can not use those as NSArrays.
The PDF API sure looks like a standard Core Foundation compatible one, but it really is not.

From Apple's documentation,
CGPDFArray header file defines an
opaque type that encapsulates a PDF
array
so it cannot be used as a NSArray.

Related

How do i refer to methods inside a UIVIiw?

I have imported my .h file into a 2nd one, but in the 2nd one i'm trying to do:
FirstClass *firstClass = [FirstClass alloc] init];
[firstClass iconWithType:test];
To match this:
-(void)iconWithType:(NSString *)iconType
But it's not listing iconWithType as a suggestion and i get a warning saying it might not respond to that.
How can i get this to work properly?
My FirstClass is a UIView.
In your FirstClass.h file do you have the method definition in the interface?
I.e.
#interface FirstClass : NSObject {
}
- (void)iconWithType:(NSString *)iconType;
#end
Additionally, the name of the method implies something should be returned. However, it is marked as void.
I'm guessing you just have a return type mismatch. Take a look: does -iconWithType: actually return void? or does it return a UIImage or something else besides?

How do I create an NSArray with string literals?

I'm attempting to create an NSArray with a grouping of string literals, however I get the compile error "Initializer element is not constant".
NSArray *currencies = [NSArray arrayWithObjects:#"Dollar", #"Euro", #"Pound", nil];
Could someone point out what I'm doing wrong, and possibly explain the error message?
New syntax for creating an array with string literals:
NSArray *currencies = #[#"Dollar", #"Euro", #"Pound"];
To fix your complication error the code must be in a method. If you want to use it statically then create a class method that follows the singleton pattern.
This isn't a problem with the NSArray creation itself (you would get the same error if you wrote [NSArray array] instead), but with where you've written it. I'm guessing this is a global or file-static NSArray. In C, that kind of variable has to have a constant initializer — meaning not a function call (or, by extension, a method call). The solution is to put the actual creation and assignment of the array into a method that will be called before you need the array, such as initialize.
It sounds like Chuck has spotted the problem. One thing you want to be aware of though in coding your solution is that you'll want to avoid storing an autoreleased instance of NSArray in a static variable. Also, a common pattern for these situations is to write a class method that creates and returns the value stored in the static variable, like so:
+ (NSArray *)currencies
{
static NSArray *_currencies;
// This will only be true the first time the method is called...
//
if (_currencies == nil)
{
_currencies = [[NSArray alloc] initWithObjects:#"Dollar", #"Euro", #"Pound", nil];
}
return _currencies;
}
Although this is old, please notice that Apple committed a new patch to the llvm project adding support for new Objective-C literal syntax for NSArray, NSDictionary and NSNumber.
See here and here
I'm a newbie in objective-c, but I think that the correct code is:
NSArray *currencies = [[NSArray alloc] initWithObjects:#"Dollar", #"Euro", #"Pound", nil];
I am not sure tho.
There's nothing wrong with that code. Are you sure the error is being produced at that line?

objectForKey stringValue crashing my app?

I have a class that I use to setup objects in an array. In this class I have a custom "initWithDictionary", where I parse a JSON dictionary. However, as I am running into NSNull, this crashes my app. To get around this, I set up a class that handles exceptions, so when a string is NSNull, it's replace it with #"". or -1 for integers.
This is my NullExtensions class:
#interface NSNull (valueExtensions)
-(int)intValue;
-(NSString *)stringValue;
#end
#implementation NSNull (valueExtensions)
-(int)intValue {
return -1;
}
-(NSString*)stringValue {
return #"";
}
#end
However, in my initWithDictionary method, the following code crashes my app:
self.bookTitle = [[parsedDictionary objectForKey:#"book_title"] stringValue];
It doesn't work regardless of the object in the parsed dictionary being NSNull or containing a valid string. Only if I do the following (and the string is not null):
self.bookTitle = [parsedDictionary objectForKey:#"book_title"];
Is stringValue incorrect in this case? And if so, how do I use it properly in order to setup proper NSNull replacements?
Thx
You really really don't want to add a category to NSNull that adds such common methods. That will change the behavior of NSNull for all instances in the application, including ones created by the underlying frameworks solely for their private use.
If you need a value class that represents the notion of "value doesn't exist and therefore I'm going to return these default values instead", create a class or instance that represents exactly that.
As for why it crashes, I couldn't tell you without seeing the actual details of the crash.
And, yes, it really is THAT bad to add a category to a class that adds such a common method. All it takes is one bit of code in a plug-in or framework that does:
if ([fooMaybeNull respondsToSelector: #selector(intValue)] bar = [fooMaybeNull intValue];
Not terribly farfetched -- I have had to debug nasty crashers or misbehaviors due to exactly this kind of willy-nilly category addition.
If you are going to add methods to a class via categories, prefix your method names so as to isolate them from existing functionality. It is still fragile, but manageably so.
Instead of creating categories on NSNull, for which you would also have to add a similar category to NSString (that's why it crashes, because real strings do not respond to stringValue) - instead try creating a helper category on NSDictionary like "stringForKey" that uses the code Johan posted and returns an NSString, probably also should enforce all other types get mapped to empty strings as well.
The NSNull extensions you have written look ok to me but using a method like stringValue may be confusing since other classes like NSNumber use this.
Personally though, I think NSNull replacement in this instance is unnecessary. If you just made a quick test you can replace the NSNull where you need to. e.g.
id testObject = [parsedDictionary objectForKey:#"book_title"];
self.bookTitle = testObject==[NSNull null] ? #"" : testObject;
You are asking an NSString for its stringValue. No need to convert a string to a string.
Try this:
if (![[parsedDictionary objectForKey:#"book_title"] isKindOfClass:[NSNull class]]) {
self.bookTitle = [parsedDictionary objectForKey:#"book_title"];
} else {
self.bookTitle = #"";
}
Edit: You should not use the category on NSNull you created. You don't need it, nor should you want it. If the source for the dictionary inserts NSNull instances, go ahead and use my code above. Normally you would expect to simple have no value inserted for the key, at which time you can simple see if [parsedDictionary objectForKey:#"book_title"] returns anything.
Are you sure that the dictionary is returning [NSNull null]? By default, dictionaries return nil, not [NSNull null], when an value isn't found for a key.
However, in my initWithDictionary method, the following code crashes my app:
self.bookTitle = [[parsedDictionary objectForKey:#"book_title"] stringValue];
It doesn't work regardless of the object in the parsed dictionary being NSNull or containing a valid string.
That makes sense, since stringValue is not a valid method on NSString. It will work for NSValue and its subclasses, but not NSString.

How to call a method without an instantiated object

This is an objective-c question.
I would like to call a method in an object, but there is no instantiation of the object. Is this possible?
The method I want to call is not a class method.
You can't call a method on an object that doesn't exist. But you can call a method on a class even if you have no instantiated objects of that class. (That's what alloc is in #fbrereton's answer -- a class method).
Class methods are declared and defined with a + instead of a -, are called on the class rather than the instance, and cannot access self or any instance variables in the class (for reasons that should be obvious).
Unless the method is static you will not be able to do this. static routines in Objective-C will be prepended with a +. For example NSObject provides these two routines (among many):
+ (id)alloc; // static - an NSObject instance is not required
- (NSString*)description; // nonstatic - an NSObject instance is required
One would make the respective calls like so:
NSObject* result = [NSObject alloc];
NSString* desc = [result description];
Sorry to nit-pick Chris' terminology, but we don't call a method on an object in Objective-C, we send a message to an object. When you send a message, the runtime will look up the appropriate method and call it. The distinction matters.
Perhaps you just want a plain-old C function. If you don't want a class method, and you don't want an instance method, that appears to be your only option. Don't be afraid of using C functions in Objective-C. Every technique has its place.
You can indeed invoke an instance method without an instance, provided it is functionally a class method (that is, it accesses no instance variables). Here's an example:
/*
Compile with:
gcc -framework Foundation inst_method_without_inst.m -o inst_method_without_inst
*/
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Foo : NSObject {
}
- (BOOL)doSomethingWithThis:(NSString *)this;
#end
#implementation Foo
- (BOOL)doSomethingWithThis:(NSString *)this {
NSLog(#"Look, it's this: %#", this);
return YES;
}
#end
typedef BOOL (*my_sel_t)(id, SEL, NSString *);
int
main(void) {
Class cls = [Foo class];
SEL my_sel = #selector(doSomethingWithThis:);
Method m = class_getInstanceMethod(cls, my_sel);
// You could also use +[NSObject instanceMethodForSelector:] to get |m|,
// since |cls| is a kind of NSObject.
my_sel_t f = (my_sel_t)method_getImplementation(m);
BOOL result = f(nil, my_sel, #"Hello from an instanceless instance method invocation!");
NSLog(#"result: %d", (int)result);
return EXIT_SUCCESS;
}
You could get it to work even if the instance method accesses instance variables by allocating memory for it to work with (using either +alloc or class_getInstanceSize() plus malloc()) and passing a pointer to that memory as the first id argument to the implementation instead of nil.
While this is entertaining as an exercise, I can't think of a good reason to not just instantiate the class and use the standard messaging syntax and compiler support. In fact, the only reason we couldn't just do [(Foo *)nil doSomethingWithThis:#"BOO!"] here is that objc_msgSend() special-cases messages to nil with the result that NO is returned and nothing happens.

Public scope in Objective-C?

I’m sure this is a simple one, but it’s been elusive so far, and I’m stumped ...
How do I declare an Ivar so that it’s accessible from ALL Classes in a project?
[Don’t know if it matters, but the ivar in question is (an instance of) my Model class, whose data needs to be accessible to various view controllers.]
Best as I can tell from "The Scope of Instance Variables” in The Objective-C 2.0 Programming Language
... this would be by using the “#public” directive.
So I’ve tried this in the #interface block where the ivar is declared:
#interface ...
...
#public
ModelClass *theModel;
#end
... But when I try to refer to “theModel” in a different class, the compiler doesn’t auto-complete, and when I type it in anyway, the compiler shows:
“Error: ‘theModel’ undeclared (first use in this function)”.
I assume this is a question of Scope, and that I haven’t made the ivar available appropriately, but how? Somehow I need to access this, or make its pointer available somehow.
Any ideas would be VERY much appreciated. Many thanks!
Perhaps you forgot to put the instance variable inside the braces of the class where all instance variable declarations go?
#interface Foo : NSObject {
// other instance variable declarations
#public
ModelClass *theModel;
}
// method and property declarations
#end
Also, can you show us the code of how you are trying to access the instance variable from elsewhere? The proper syntax should be:
myFooInstance->theModel
where myFooInstance is a value of type "Foo *"
I make properties available to all views managed by a Tab Bar via a singleton representing my data model. This is efficient and allows all Views access to the data (as well as any other application elements. Creating the singleton is straightforward (there are a ton of examples on S.O.). The you just request the instance and get the property values you need.
Here is a framework fro creating the Singleton. The key points are the static instance and the fact that you do the initialization as [[self alloc] init];. This will ensure the object gets cleaned up correctly. All the methods at the bottom of the class are standard from the SDK Docs to make sure release calls are ignored (because the object is shared globally).
Singleton Boilerplate (ApplicationSettings.m):
static ApplicationSettings *sharedApplicationSettings = nil;
+ (ApplicationSettings*) getSharedApplicationSettings
{
#synchronized(self) {
if (sharedApplicationSettings == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedApplicationSettings;
}
+ (id)allocWithZone:(NSZone *)zone
{
#synchronized(self) {
if (sharedApplicationSettings == nil) {
sharedApplicationSettings = [super allocWithZone:zone];
return sharedApplicationSettings; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
You cannot access iVars from any other class.
You have to declare a getter/setter method to change or view a variable from another class - what you are really looking for are properties, that make it easier to define and access these getter/setter methods.
In your example above, you'd have the property defined just after the block that defines the local variable in the header file:
#property (nonatomic, retain) ModelClass *theModel;
In the implementation file you'd have the getter/setter created with the #synthesize statement just after the #implementation line:
#synthesize theModel;
Then if you have an instance of your class created, you access the class instance variable like so:
myInstance.theModel = [[[ModelClass alloc] init] autorelease];
The reason #public & #private are in there are to define visibility for subclasses (which, being extensions of that class type also get all the class local variables defined by a superclass), not for any random class.
The standard Objective-C way of doing it is to have a class method that returns the ivar
In your .h file:
+ (id)defaultModel;
and in your .m file:
static ModelClass * defaultModelInstance;
#implementation ModelClass
+ (id)defaultModel {
if (!defaultModelInstance) {
defaultModelInstance = [[ModelClass alloc] init];
}
return defaultModelInstance;
}
#end
although this will need tweaking if you need a specific ivar instead of just "a ivar that's always the same"
this type of design is used by many Cocoa classes i.e. [NSWorkspace sharedWorkspace]
Think a C global variable.
Adding:
extern ModelClass* theModel;
after the #end in the header will make the variable visible anywhere you include the header.
In the ModelClass.cpp file add:
ModelClass* theModel;
before the class implementation.
The variable will still have a value of nil until you allocate and initialize it though and you will be resposible for ensuring that it gets deallocated at the correct time.
THANK YOU ALL for the very helpful discussion on this topic! Clearly there are several ways to approach things here, so this is a very useful assortment of techniques.
Just to let y'all know that in researching this issue further, I ran across a couple of other very helpful pages, listed below. They include mention of the NSNotificationCenter, which I hadn't heard of before; as well as the idea of the "dependency injection" design pattern.
The idea is to keep "low coupling"(1) between the classes, making the code more modular & better for unit testing.
And while the 'notification' pattern sounds like a great idea, in this case it may be a bit overkill, considering that I only need ONE instance of the data model throughout the run of the app, and it doesn't change throughout.
Finally, even though the "#public" compiler directive is well-documented in Apple's Obj-C guide(2), I later found a fascinating edict in a different doc stating that it shouldn't be used! Quoted from Apple's own Cocoa Fundamentals(3):
"Give the proper scope to your instance variables. Never scope a variable as #public as this violates the principle of encapsulation. ..."
(Strange that they don't mention this in their 'Objective-C 2.0' guide where the directive is actually explained.)
Anyway, here are a couple of other links I found to be full of some great insights as well. FYI:
S.O.: "What’s the best way to
communicate between
viewcontrollers?"(4) <<
CocoaWithLove: "Five approaches to
listening, observing and notifying in
Cocoa"(5)
CocoaWithLove: "Singletons,
AppDelegates and top-level data"(6)
Hope these help. Anyway, thank you all again!
Best,
rond
P.S. Yikes! It won't let me post more than one inline hyperlink, so I'm listing them here instead. Obviously, they’re all prefixed by “http://” ... :O
(1): en.wikipedia.org/wiki/Coupling_(computer_science)
(2): developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Articles/ocDefiningClasses.html#//apple%5Fref/doc/uid/TP30001163-CH12-TPXREF127
(3): developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/AddingBehaviortoaCocoaProgram/AddingBehaviorCocoa.html#//apple_ref/doc/uid/TP40002974-CH5-SW12
(4): stackoverflow.com/questions/569940/whats-the-best-way-to-communicate-between-viewcontrollers
(5): cocoawithlove.com/2008/06/five-approaches-to-listening-observing.html
(6): cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html