NSImage size problem - objective-c

I'm using the same image resource in two different controllers. In both controllers the image is shown in different sizes, the problem is that once the image is shown in a smaller size than the original, the next time I get the image by [NSImage imageNamed:#"resource.png"] the image size is set to the last size it took. I tried by invoking the recache method on NSImage and also tried to set the cache mode to any the posible value, but it didn't work.
Any ideas?

You should never modify an instance of NSImage obtained from imageNamed:. The returned instance is shared with other clients, so it should not be changed.
If you have to setSize: on the image, just make a copy and use that one:
NSImage *image = [[[NSImage imageNamed:#"foo.png"] copy] autorelease];
[image setSize:(NSSize){128, 128}];

The thing is that
[NSImage imageNamed]
As you mentioned is in the cache, and as long as it is in the cache it will return the cached image so what you need to do is first released the previous reference or use the object's setName method and setting to nil. Here is the documentation reference:
The NSImage class may cache a reference to the returned image object for performance in some cases. However, the class holds onto cached objects only while the object exists. If the image object is subsequently released, either because its retain count was 0 or it was not referenced anywhere in a garbage-collected application, the object may be quietly removed from the cache. Thus, if you plan to hold onto a returned image object, you must retain it like you would any Cocoa object. You can clear an image object from the cache explicitly by calling the object’s setName: method and passing nil for the image name.

Related

How can I change the value of an NSImage?

I am a beginning Objective-C programming programmer, and I want to change the value of an NSImage. That means that if I have an NSImage that is a NSStatusUnavaliable, how could I make it a NSStatusAvaliable? Thanks!
So I believe what you're asking is: Somewhere you have an NSImage instance that was initialized like:
NSImage *image = [NSImage imageNamed: #"NSStatusUnavailable"];
And you would like to use the image that is mapped to #"NSStatusAvailable".
Without going into details, you really want to assign your image to [NSImage imageNamed: #"NSStatusAvailable"];
Take a look at the docs for NSImage. Specifically what you might find useful is the imageNamed: class method.
Discussion
This method searches for named images in several places, returning the first image it finds matching the given name. The order of the search is as follows:
Search for an object whose name was set explicitly using the setName: method and currently >resides in the image cache.
Search the app's main bundle for a file whose name matches the specified string. (For information on how the bundle is searched, see ““Accessing a Bundle's Contents”“ in >Bundle Programming Guide.)
Search the Application Kit framework for a shared image with the specified name.
But also since you're new, just reviewing Objective-C docs on object life cycle will be very useful to understand what's going on.
Swift:
let 🈯️ = NSImage(named: .statusAvailable)
let 🅿️ = NSImage(named: .statusPartiallyAvailable)
let 🔴 = NSImage(named: .statusUnavailable)
let 🔶 = NSImage(named: .statusNone)

UIImage causing memory leaks

Instruments is telling me that alot of memory is being allocated when I rapidly set the image name of a UIImageview in my app. I have a UIImageView that changes its image name every frame in my game. When profiled with zombie checking in instruments, the app seems to be constantly gaining live bytes at an enourmous rate. Is there a way that I can deallocate the UIImageView's current image to stop it from doing this? I am using ARC.
My code to assign the UIImageView's image is as follows:
aPlanet.image = [UIImage imageNamed:tempPlanetName];
Where aPlanet is the UIImageView and tempPlanetName is the name of the image. This is called every frame.
[UIImage ImageNamed:] method loads the image into image view and adds this newly created uiimage object to autorelease pool. To get rid of this problem you should use -
NSString *imgPath = [NSBundle mainbundle] pathForResource:#"imageName" ofType:#"png"];
aPlanet.image = [[UIImage alloc] ]initWithContentsOfFile:imgPath];
if you are using arc then you don't need to bother about releasing this newly allocated object of uiimage which was created using initWithContentsOfFile: method.
When you use UIImage imageNamed: it will load and cache that image file. This is intended for reuse of icons and other image resources that will be utilized more than once in your application.
Apart from it seeming somewhat unusual to update an image view with a new image every frame, you should look into alternative means of loading images that you will not need more than once - or even if you do, when you need more control over its lifecycle.
For example have a look at UIImage imageWithContentsOfFile: (documented here: Apple Developer Library Reference). It explicitly states that this method will not do any caching of the image contents.
I hope this helps you, but for every frame I doubt that your performance will be good enough with this approach, but this is probably the topic of a different question should the need arise.

Prevent instance variables from being cleared on memory warning

I've got a relatively simple problem that's been evading solution for some time. I have a view controller and an associated XIB. The view controller is called FooterViewController. FooterViewController's view is set as the footer view of a tableview.
FooterViewController's view includes a label for showing feedback to the user. I would like this label to persist until its value is changed by my application. Under normal circumstances, it does. However, I've just begun testing with memory warnings, and I've found that after the view is unloaded in response to a memory warning, the label is cleared.
Here's what I've tried so far to solve the problem: in FooterViewController's viewWillUnload method, I store the label's text in an instance variable called statusString:
- (void)viewWillUnload
{
statusString = [statusLabel text];
testInt = 5;
NSLog(#"View will unload; status string = %#; testInt = %d",
statusString, testInt);
[super viewWillUnload];
}
Note that I've also set another instance variable, declared as NSInteger testInt, to 5.
Then, in FooterViewController's viewDidLoad method, I try to set the label's text to statusString:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Just before setting label, statusString: %#; testInt: %d",
statusString, testInt);
[statusLabel setText:statusString];
NSLog(#"View did load.");
}
However, this does not work. Further, in the log after simulating a memory warning, I see:
View will unload; status string = Invalid IP address Error code: 113; testInt = 5
(Note that "Invalid IP address Error code: 113" is the correct value for statusString)
Then, after navigating to FooterViewController again, I see:
Just before setting label, statusString: (null); testInt: 0
This indicates to me that for some reason, the instance variables of FooterViewController are being reinitialized when the view loads again. A final note: the method initWithNibName:bundle: is being called each time the view must reload, though I expect this; after all, the view must be reloaded from the NIB.
So, my questions are these:
Why do these instance variables appear to be nullified or zeroed in the process of unloading and reloading the view?
If I'm doing something incorrectly that's causing this nullification, what is it?
If I'm not doing anything incorrectly, and this is normal behavior, how should I handle maintaining state between loads of the view?
Thanks,
Riley
The statusString looks like a weak reference, not a strong property. It can not retain the labels's text, which gets deallocated with the label when the view is unloaded. That's why you get first a correct value (before the label is deallocated), and null later (after the label has been deallocated, and the weak ref nullified). Turn your statusString into a strong property, and that ARC magic won't bite you any longer.
It looks like you need to be using didRecieveMemoryWarning instead of viewDidUnload, since viewDidUnload is not guaranteed to be called in the event of a memory warning. If the crash is exiting the app completely then you need to be writing the data to disk using something like coreData. Save your data here and then call the super so the view will still be released. Hope that helps.
I figured out what was going on, finally. The issue was that I called the allocation and initialization methods for FooterViewController in its parent view controller's viewDidLoad method. When the views were dumped and subsequently reloaded, my view controller was re-initialized! This destroyed the original FooterViewController, which maintained the instance variables I needed, and replaced it with a brand-new VC.
The solution was to move [[FooterViewController alloc] init] to the init method of FooterViewController's parent VC, so that the initialization was only performed once per run cycle.
I've learned my lesson: don't reinitialize your view controllers unless you really mean to do so. As such, be very careful where you put your calls to the initializer in parent view controllers.
Thanks for the help I got from the two answerers.

awakeFromFetch with non-managed property

I have an NSManagedObject subclass with some image data in an NSData property (imageData), which is automatically persisted. After loading the data from the network I also set a custom NSImage property (image) with an image created from the data.
The problem is that if the object is a fault, awakeFromFetch is not called when I access the image property, since it is not handled by Core Data. I can of course override the image accessor and make sure the properties are loaded (by accessing imageData) but it would be nice if there was a way to have awakeFromFetch invoked as normal. Any suggestions?
As per TechZen's suggestion, I now execute the fetch request with returnsObjectsAsFaults set to NO. It's of course not ideal to pull all data from the cache each time the object is fetched, but in my case I always use the data immediately so it's acceptable.
You seem to want to do two contradictory things, you want the main entity to remain fault but be able to access its attributes. You cannot do that. A fault by definition has not attributes/properties because it is just a placeholder in the object graph. If you want your main object to remain a fault, you should move the UIImage to its own entity and link it to the main object. You should then be able to walk the object graph to obtain the UIImage without triggering the loading of the main object's fault placeholder.

Releasing UIImage imageNamed

When I use UIImage imagenamed: should I set the variables which hold UIImages to nil before exit? I notice that sometimes when I switch between views which have UIImages, the memory keeps growing and growing with each switch back and forth.
Setting variables to nil is not necessary.
Setting properties to nil (self.property = nil;) will release them if they're declared #property (retain).
Since +imageNamed: doesn't start with "alloc", "copy", "new", or "retain", you don't have to release it. It's possible things are staying in memory because the space isn't needed. Are you seeing any leaks or just memory usage?
Setting UIImage variables to nil won't do anything particularly useful. Also, you shouldn't release the image returned from +imageNamed:, since the method name does not imply that you have ownership of the returned object.
Cocoa maintains an image cache. Subsequent calls to imageNamed: will return the same UIImage object if it is loaded already (since UIImage objects are immutable), otherwise it will load it again into the cache. The lifetime of the image in the cache is up to Cocoa to decide. In low-memory situations the image data may be purged. Even though the actual internal image data is purged from the cache, the object that you have is still able to reference the image (Cocoa will reload the image data if it was purged from the cache). It is explained throughout the UIImage documentation.
If your memory usage is consistently growing then the leak is probably coming from somewhere else.