in objective C, I can do the following
UIImage *myImage = [UIImage imageNamed:#"myPhoto.jpg"];
variable.image = myImage;
and this works just fine. but the object named "myImage" was never initialized, and the UIImage never had any memory allocated and yet the code still works..
Can someone explain what's going on here?
Yes, the object was initialised. The imageNamed: method allocates and initialises an object, sends it an autorelease message, then returns the memory address to you. You store that memory address in the pointer called myImage.
myImage and the object are two different things. myImage merely points at a memory location. It is not the object itself.
You can pass around objects without assigning them to variables, and you can assign one object to many variables.
Consider this:
UIImage *imageOne;
UIImage *imageTwo;
imageOne = [UIImage imageNamed:#"myPhoto.jpg"];
imageTwo = imageOne;
The image wasn't copied. There is only one object in existence. Both variables point to it.
Now consider this:
NSLog(#"%#", [UIImage imageNamed:#"myPhoto.jpg"]);
You didn't assign it to any variable. But the object still existed, right?
Have a look in the documentation for UIImage. Under the heading "Cached Image Loading Routines" and "Creating New Images" are some methods with a + where you might expect a -. This means that calling these methods is the same as calling [[[UIImage alloc] init] autorelease];, though some of them do more (as in custom initialization).
Lots of other Objective C objects have similar methods, for example, NSArray has a method +(id)array that creates and returns an empty array. Your main indicator here is the + instead of the -, but I always check the documentation to check how the initialization is handled and to make sure the object is autoreleased.
Related
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
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!!
I can construct a UIImageView object with myImageView = [[UIImageView alloc] initWithImage:image];
Following application activity affecting the display, if I want to change the image on the UIImageView. I can do so by reassigning it with myImageView.image = someNewImage. However this doesn't seem to update the frame dimensions. I can modify those manually, but I have observed in practice that calling [myImageView initWithImage:someNewImage] does that for me, and has the advantage of being terser.
However I not sure if it is officially a breach of protocol in Objective C to make multiple calls to init methods on an object constructed by a single alloc. I wouldn't use it unless it was safe (guaranteed not to crash or cause leaks). Is there evidence that it is unsafe?
My research so far...
This article gives general detail about 'alloc' and 'init' on objects
http://developer.apple.com/library/mac/documentation/cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html
This is related SO question
Why are alloc and init called separately in Objective-C?
This blog article warns suppliers of objects that their init methods may be called multiple times as an effect of the initialization process.
http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/
No.
-init is written assuming that it is only called once. For example, -initWithImage quoted in Apple's documentation you quoted is implemented as
- (id)initWithImage:(NSImage *)anImage {
...
if (self) {
image = [anImage retain];
}
return self;
}
This assumes that the ivar image doesn't point to a retained object. If called twice, it leaks image.
Call -init... only once per alloc, and call -init... immediately after alloc by combining them as usual:
SomeClass* foo=[[SomeClass alloc] init...: ... ];
You should never separate them, because [anAllocedObject init...] might return something different from anAllocedObject.
No, init (or one of it's variants) should only be called once on any object, as Yuji explained. As far as your UIImageView issue goes, I believe you can just call [imageView sizeToFit] after assigning your new image, and it will automatically resize it for you.
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.
I get an 'EXC_BAD_ACCESS' error when trying to access variable in a function other than the one it was set in
The variable is set in the 'awakeFromNib' function:
//Retrieve Session-ID
sessionID = [self getSessionID];
And accessed in 'searchBtnClick':
NSLog(#"Commening search (%#)",sessionID); // This causes the error
The variable itself is defined in the header:
NSString *sessionID;
Can someone suggest what might be wrong with that?
The part which of getSessionID which returns the value:
NSString *pC = #"";
// Separate Session ID
pC = [initCookie substringFromIndex:10];
pC = [pC substringToIndex:32];
NSLog(#"Got session ID : %#",pC);
return pC;
Your -getSessionID method is returning an autoreleased variable—when you try to access the pointer again later, the string's already been deallocated and so the reference is no longer valid. You need to call -retain on the variable when you first retrieve it, like this:
sessionID = [[self getSessionID] retain];
Then, later, in your class's -dealloc, you need to balance the retain with a release:
[sessionID release];
If getSessionID follows normal Cocoa conventions, it returns an autoreleased object. You need to retain it, or sessionID will become a dangling pointer as soon as the autorelease pool is drained (probably at the end of the event loop).
If you are new to Objective C and Cocoa, you should make sure to read the Apple documentation about the memory model.
I had similar prob, it crashes when you have not allocated any memory. Releasing it like this:
UIImage *lObj_image = [UIImage imageNamed: #"bluebar.png"];
.
.
.
[lObj_image release];
Check in your viewdidload()