How can I correct this 'Incompatible Obj-c types' warning? - objective-c

Specifically the warning is: "Incompatible Objective-C types 'struct NSString *', expected 'struct UIImage *' when passing argument 4 of 'objectWithType:name:code:image' from distinct Objective-C type". It follows a line that looks like so:
[Object objectWithType:#"Type" name:#"Name" code:#"0001" image:#"image.png"],
So, I understand that I created the class Object to take type UIImage, but I am providing it with type NSString. Here's the problem: I don't know how to indicate the image differently than its file name.
(Apologies if this is a basic problem. I'm new to this and trying to look for solutions before posting here. Any help you can offer is appreciated.)

You actually need an instance of UIImage, which you understand. So, the class method imageNamed: is typically used for this:
[Object objectWithType:#"Type" name:#"Name" code:#"0001" image:[UIImage imageNamed:#"image.png"]];
Another option (since you are using all strings here) might be to rewrite your method so that it takes a name instead of an image and then create the image inside the method implementation. So you might define the method:
- (void)objectWithType:(NSString*)type name:(NSString*)name code:(NSString*)code imageName:(NSString*)imageName
{
UIImage* theImage = [UIImage imageNamed:imageName];
// do whatever
}

Use [UIImage imageNamed:]. Will only work for images that are part of your project.
[Object objectWithType:#"Type" name:#"Name" code:#"0001" image:[UIImage imageNamed:#"image.png"]]

If 'image.png' is included in your project bundle, then you can do this to pass a UIImage instead of an NSString:
[Object objectWithType:#"Type" name:#"Name" code:#"0001" image:[UIImage imageNamed:#"image.png"]];

Related

How the Obj C object's property name may go through several calls and become a selector name?

I have a small piece of code using MapBox framework:
if (tmpItem.itemTyype > 0 && tmpItem.itemTyype <= 12)
marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]]];
else
marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:#"PoiSmall"]];
This code leads to an exception:
-[RMQuadTreeNode itemTyype]: unrecognized selector sent to instance 0xe9aaf00
The RMQuadTreeNode class is a MapBox' class and never be used by myself in my code. Note the unrecognized selector name - this is the name of my tmpItem.itemTyype property!
If I replace this code with the following:
if (tmpItem.itemTyype > 0 && tmpItem.itemTyype <= 12)
image = [UIImage imageNamed:[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]];
else
image = [UIImage imageNamed:#"PoiSmall"];
marker = [[RMMarker alloc] initWithUIImage:image];
then error is gone. It's not a problem to replace one fragment of code with another one, but I want to know HOW the 'itemTyype' property became a selector name and later was called somethere inside MapBox framework. The property name went thru NSString call, UIImage call, RMMarker call and do-not-know-how-many another calls to reach RMQuadTreeNode. HOW THIS CAN BE?
This knowledge is not necessary for me to solve this particular problem, but it is necessary to know Objective C better.
EDIT. I have to change the question above. Adjusted question is: I want to know HOW the 'itemTyype' property was passed not only to [NSString ...] call but also somethere inside MapBox framework.
In other words, should we see something like
-[UIImage itemTyype]: unrecognized selector sent to instance 0xe9aaf00
or
-[RMMarker itemTyype]: unrecognized selector sent to instance 0xe9aaf00
?
Answer is NO because the [tmpItem itemTyype] is a parameter of
[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]
(ok, just rewriting it as)
[NSString stringWithFormat:#"PoiSmall%d",[tmpItem itemTyype]]
but it's not a parameter of [UIImage ...] nor [RMMarker ...] nor something else like [RMQuadTreeNode ...].
So how it was passed thru the chain of all these calls above and reached [RMQuadTreeNode ...] call? How the parameter of [NSString ...] call magically turned into a parameter of [RMQuadTreeNode ...] call?
In Objective-C dot notation for properties is just a 'shortcut' for writing accessor methods. So when you write
object.property
This is equivalent to writing:
[object property]
And
object.property = ?
is equivalent to:
[object setProperty:?]
So, back to your example,
[[RMMarker alloc] initWithUIImage:… ,tmpItem.itemTyype]]];
is the same as:
[[RMMarker alloc] initWithUIImage:[UIImage imageNamed:…,[tmpItem itemTyype]]]];
This is why you're seeing method itemTyype being called.
So object tmpItem is an instance of RMQuadTreeNode, which doesn't declare method itemTyype (or the equivalent property).
These Apple docs give a fuller explanation.
Simple explanation:
If you declare a property setter and getter methods to access this property are generated automatically (different for older Xcode versions) as well as an instance variable for this property. A setter is used to set the value of this variable, a getter is used to get the value, both so KVO can be used as well.
If you declare a property itemType like for example
#property (strong) NSNumber *itemType;
you also get in your class
- (void)setItemType:(NSNumber*)newValue;
- (NSNumber*)itemType;
as well as a variable
NSNumber *_itemType;
When you use the dot notation of Objective-C 2.0 like self.itemType these getter/setter methods are actually called.
This is a short overview, you should definitely invest time and read up on Objective-C like for example this here

How to override a method in framework's class (runtime)

problem:
framework will cache the image data when above:
[UIImage imageNamed:]
I don't want the caching happen,so I can replace it by
[UIImage imageOfContentOfFile:]
Seems solved,but after my tests,in xib's instantiate progress, framework uses imageNamed: rather than imageOfContentOfFile.
That is, images loaded by xib still cached.
So I try to override the UIImage's imageNamed: method, make all this method DO NOT CACHE.
--try1. category:
#implementation UIImage (ImageNamedNoCache)
+ (UIImage *)imageNamed:(NSString *)name;
{
NSString* path = [[NSBundle mainBundle] pathForResource:name ofType:nil];
return [UIImage imageWithContentsOfFile:path];
}
#end
--result:warning."Category is implementing a method which will also be implemented by its primary class"
--try2. runtime replace the method's imp
//get super method
Method method_imageNamed = class_getClassMethod([UIImage class], #selector(imageNamed:));
//get my method
Method method_myImageNamed = class_getClassMethod([self class], #selector(myImageNamed:));
//get my imp
IMP imp_myImageNamed = method_getImplementation(method_myImageNamed);
//set super method's imp to my imp
method_setImplementation(method_imageNamed, imp_myImageNamed);
--result
these too try only work when I invoke [UIImage imageNamed:]
but xib's instantiation doesn't work.
help me, thanks
Sounds like a job for method swizzling. I find it dangerous and unnecessary, but if you are so inclined, it is an option. Use at your own risk.
The category method should be fine, just give your method a different name. It's my understanding that it's not good practice to override methods in a category, but adding a new one is fine.
Correct me if I do not interpret correctly, but it looks like your core problem is that you are trying to get framework (i.e., Apple code) to call imageWithContentsOfFile instead of imageNamed when loading xibs.
A third option might be to leave the UIImageViews in the xib 'blank' as a placeholder, and then programmatically loading UIImages into them later (for example in the FileOwner's init code), perhaps with the help of the 'tag' property + a dictionary to determine the right image, if it's convenient. Or you could even create both the images and their views dynamically instead of keeping them in the xib file. Obviously there are lots of tradeoffs here.
Your solution isn't good :) Caching is used for good reasons.
Just don't use [UIImage imageNamed:] In YOUR code, and all will be fine
If you Actually need not to cache your images, you should use method #2.
NOTE: Images(actually, all objects) from XIBS are being loaded with -[object initWithCoder:], and not with [UIImage imageNamed:] or [UIImage imageWithContentsOfFile:]

Is it necessary to use a temporary variable when setting a properties' value?

I have a (retained) UIImage property that is being used to hold a user selected image.
This is the code I have at present when the user makes a selection:
- (IBAction) selectImage1 {
UIImage *image = [UIImage imageNamed: #"image1-big.png"];
self.bigImage = image;
}
but I'm wondering if it is possible to omit the use of the temporary variable convenience method and just do this:
- (IBAction) selectImage1 {
self.bigImage = [UIImage imageNamed: #"image1-big.png"];
}
If there are problems with this second method (I'm guessing something to do with memory management), could someone please explain?
Thank you!
The second way is perfectly fine. The line UIImage *image = [UIImage imageNamed: #"image1-big.png"]; gives you a variable image that is auto-released. Assigning it to your ivar via the self.bigImage = image calls bigImage's setter method which retains the value. Thus the line self.bigImage = [UIImage imageNamed: #"image1-big.png"]; is equivalent to the more verbose way.
There is no difference in terms of memory management between the two snippets you posted; unless you get really specific about retain counts in between the two lines in the first snippet.
In an ARC environment, the local variable will be a 'strong' pointer, however it is released when the method leaves scope. In the second snippet, there is no intermediate retain/release'd pointer, and so may actually be slightly more efficient.
The places I have seen the first snippet's technique be necessary are when you have a weak pointer (i.e. a weak #property) where setting self.foo = [UIView ... would immediately allow it to be released. In these cases it is better to use a local variable to keep it around while you work with it:
UIView *someFoo = [UIView...
[self addSubview:someFoo];
self.someWeakProperty = someFoo;
compare with:
self.someWeakProperty = [UIView...
[self addSubview:self.someWeakProperty]; // it's already nil!!

NSImage initWithData not found?

In NSImage class reference it is said that there exist method initWithData, but why xCode says it doesn't exist?
I use it like this:
NSData *imgData = [[imageView image] TIFFRepresentation];
fotoImg = [NSImage initWithData:imgData];
And i get warning:
Class method '+initWithData:' not found (return type defaults to 'id')
Why is this happening?
You are calling initWithData on the class when you should call it on an instance of that class. Do this: [[NSImage alloc] initWithData:imgData];
It is very important to learn the difference between member functions and class functions (or in Obj C terms "messages"). I recommend http://www.amazon.de/Programming-Objective-C-Automatic-Reference-Developers/dp/0321811909/ref=pd_sim_sbs_eb_1/276-1733140-9413556.

strange run time error message from CIImage initWithContentsOfURL

When executing the following code I receive a run time error when the code executes the second line of code. The error (which shows up in the debugger) says: [NSButton initWithContentsOfURL:]: unrecognized selector sent to instance 0x100418e10. I don't understand this message, because it looks to me (based on my source code) like the initWithContentsOfURL message is being sent to the myImage instance (of the CIImage class) ... not NSButton. Any idea what is going on?
If it matters ... this code is in the Application Controller class module of an Xcode project (a Cocoa application) -- within a method that is called when I click on a button on the application window. There is only the one button on the window ...
// Step1: Load the JPG file into CIImage
NSURL *myURL = [NSURL fileURLWithPath:#"/Users/Adam/Documents/Images/image7.jpg"];
CIImage *myImage = [myImage initWithContentsOfURL: myURL];
if (myImage = Nil) {
NSLog(#"Creating myImage failed");
return;
}
else {
NSLog(#"Created myImage successfully");
}
This line
if (myImage = Nil) {...
Does assignment instead of comparison
Also, don't put a space before parameter for your method in question. And it should be something like this:
CIImage *myImage = [[CIImage alloc] initWithContentsOfURL:myURL];
CIImage *myImage = [myImage initWithContentsOfURL: myURL];
You have not initialized the myImage variable, but you are sending its value an initWithContentsOfURL: message. When, by chance, it contains the pointer to an existing object (such as existing NSButton object), the exception in your question occurs.
If you are really unlucky, the object you end up sending the message to will respond to initWithContentsOfURL:, in which case this will re-initialize this object with a different URL. Depending on the URL, it may make the object have the wrong contents, or release itself. Either way, it will probably cause several leaks, and will cause a crash, either by sending later CIImage messages to an object that is still not a CIImage (simply a re-initialized other object), or by sending messages to an object that released itself and so is now dead.
The solution is, as Eimantas stated, to allocate a new CIImage object (by sending the CIImage class an alloc message), then send the initWithContentsOfURL: message to that object, then assign that result to the variable.
if (myImage = Nil) {
As Eimantas noted, this is an assignment, not a comparison. Yes, it is perfectly valid to assign to a variable within a condition in C (and so in Objective-C). The compiler offers a warning for this; you should turn it and a bunch of others on. The solution is to use the equality operator, ==.
Furthermore, as Wevah noted, Nil is the wrong constant to use here, since you are comparing an object's pointer to it, not a class's pointer. The correct constant is nil.