Objective-c convert a parent class to its derived class - objective-c

I have a custom class named MyImage inherit from UIImage. Now I have a UIImage object, is there any ways to convert it into a MyImage object?
Update
Sorry for being unclear.
I cannot use a category because I need to add new properties to my class.
What I want to accomplish is to just let my MyImage object point to the original UIImage object.
Update 2
I tried something like this:
- (MyImage*)initWithUIImage:(UIImage *)image {
if (self = [super init]) {
self = image;
}
return self;
}
Obvious it does not work.
Also, since UIImage does not have a method named 'initWithImage:(UIImage*)', I cannot write something like
myImage = [[MyImage alloc] initWithImage:uiImage];
I also tried
self = [image copy]
But the return value is still a UIImage object, not a MyImage object.
Update 3
In MyImage, I need to add 3 properties: url, width, and height. Since I am writing a instant messaging app, and when receiving a new image message, I only have its url, width, and height. Then I assign those to a MyImage object, and download the image in the background.
Now given an original UIImage A, I want to create a new MyImage object B, which points to the same image as A, but with those new properties unassigned. Then I manually assign url, width, and height to B.
to #rmaddy, could you tell me how to write the method
[[MyImage alloc] initWithImage:(UIImage*)]
?
One way I can come up is first convert the UIImage object to NSData, then use
[MyImage imageWithData:]
Is there a better way?

I do not necessarily recommend this as a solution, but there is indeed a way to do exactly what you are looking for. It is called ISA Swizzling. Think of it like method swizzling but for Classes instead of methods.
Take a look at the Objective-C runtime's object_setClass [link]
I won't go into it (there are better resources out there), but this is essentially how KVO works. Regardless, it would be helpful if you better described the functionality of your UIImage subclass, so that we could help describe why a custom initializer as rmaddy describes is probably the best solution.

Your MyImage class is a subclass of UIImage, so if you're creating a new instance you need to properly initialize the superclass as well. UIImage doesn't provide a method to do that from another UIImage instance, but you can use a CGImage, and you can get one of those from the existing image. So do this:
- (MyImage*)initWithUIImage:(UIImage *)image {
if (self = [super initWithCGImage:image.CGImage]) {
// put any necessary MyImage-specific stuff here
}
return self;
}

Related

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!!

How to Return a Custom Objective-C Object from a Class Method?

I have a set of Objective-C files in my app labeled Crop.h/Crop.m. Inside is a custom array of different vegetables from a video game - each vegetable has properties like a name, growing season, number of days to grow, etc.
I need to write a function which loops through this array and checks each Crop name value to see if it matches with my view title (self.title). Below is an example of two crop objects:
Crop *turnip = [[Crop alloc] init];
[turnip setName:#"Turnip"];
[turnip setSeason:#"Spring"];
[turnip setBagPrice:120];
[turnip setSaleValue:60];
[turnip setDaysToGrow:5];
[turnip setRenewableDays:0];
Crop *potato = [[Crop alloc] init];
[potato setName:#"Potato"];
[potato setSeason:#"Spring"];
[potato setBagPrice:150];
[potato setSaleValue:80];
[potato setDaysToGrow:8];
[potato setRenewableDays:0];
Then what I'm thinking is calling a new class function titled returnCropData: which takes the self.title as a parameter. I'm just not sure if this is my best method... I'd appreciate any suggestions from iOS devs.
Here's the simple class method I have so far - no data matching yet as I'm still trying to figure out which loop would be best. I'm also struggling to figure out the syntax to add an NSString parameter onto the function
+ (Crop *)returnCropData
{
Crop *turnip = [[Crop alloc] init];
[turnip setName:#"Turnip"];
[turnip setSeason:#"Spring"];
[turnip setBagPrice:120];
[turnip setSaleValue:60];
[turnip setDaysToGrow:5];
[turnip setRenewableDays:0];
// more crops here....
NSMutableArray *cropListing = [[NSMutableArray alloc] init];
[cropListing addObject:turnip];
[cropListing addObject:potato];
// add the other crops here...
return potato;
// return any Crop value for now
}
Apple's Objective-C docs recommend you use objectWithParams: grammar for class methods (constructors). So in your case, you should change your existing method name to
+ (Crop *)cropWithName:(NSString *)name
And call it with Crop *turnip = [Crop cropWithName:#"Turnip];
and then in your for loop you can check if the passed in name equals the name of your object with [name isEqualToString:turnip.name].
This will work, however it seems as though every time that method is called you're creating a ton of Crop objects - time intensive on your part, and memory intensive on the device. Instead, you should look into making a plist file that uses a dictionary to represent each kind of Crop, and then in your creation method you can use your passed in name to look up all the other information about the specified crop. Then you can just return one instance of Crop instead of instantiating a massive amount.
Here's a link to Apple documentation that explains plists:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html
Specifically, in any objects that I want to pull from a plist I define the following method that takes values from a dictionary and sets an object's properties:
- (id)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
[self setValuesForKeysWithDictionary:dictionary];
return self;
}
And then in a class where I want to access an array of data, I would create a synthesized #property and then define this method (in this case, I've got Song objects):
- (NSMutableArray *)songs
{
if (_songs == nil)
{
NSArray *songDicts = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"ChillMix" ofType:#"plist"]];
_songs = [[NSMutableArray alloc] initWithCapacity:[songDicts count]];
// Fast enumeration //
for (NSDictionary *currDict in songDicts)
{
Song *song = [[Song alloc] initWithDictionary:currDict];
[_songs addObject:song];
}
}
return _songs;
}
Here is a sample project I've developed: https://github.com/Jlawr3nc3/MusicLibraryiOS
[NSArray arrayWithContentsOfFile:] should be your new best friend. It's awesome. If you can format your XML in plist format, you're down to one. single. line. to parse data. (PS normal XML is annoying as hell to parse on iOS so this is a huge time save if you can avoid straight up XML).
Also, I'm not sure what the use case would be for creating a crop object based on a view's title. Since you're already aware of the name of the crop before the view is instantiated, you could create a Crop object and set a Crop property of the view to the crop object you created, and then in viewDidLoad just do self.title = self.crop.name;. Finally, if you've got a UITableView of crop objects you shouldn't populate the table view with static text; instead populate it with objects you create from a plist, and then in didSelectRowAtIndexPath: you can do [self.arrayOfCrops objectAtIndex:indexPath.row] to get the object you tapped on, and pass it in with the view you load. Anyway, check out Apple's sample code for more info on plists and tableviews etc, 'cause they've got a lot of information on this exact stuff.

Is it safe to call initializers multiple times on the same 'alloc'ed object?

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.

event scope

Given
#interface Canvas:NSView {
NSNumber * currentToolType;
...
}
declared in my .h file
and in the .m file
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
currentToolType=[[NSNumber alloc]initWithInt:1];
}
return self;
}
and further down
-(void)mouseUp:(NSEvent *)event
{
NSLog(#"tool value in event: %d",[currentToolType intValue]);
//rest of code
}
-(NSBezzierPath *)drawPath:(NSRect)aRect
{
NSLog(#"tool value in draw: %d",[currentToolType intValue]);
//rest of drawPath method code that uses the value of currentToolType in a switch statment
}
-(IBAction)selectToolOne:(id)sender
{
[currentToolType release];
[currentToolType = [[NSNumber alloc]initWithInt:0];
}
-(IBAction)selectToolTwo:(id)sender
{
[currentToolType release];
[currentToolType = [[NSNumber alloc]initWithInt:1];
}
The action methods are the only place where currentToolType is changed. But, for some reason, it seems to be a different instance of currentToolType in the mouseUp. I did not write (or synthesize) accessors for the var as it is used only by itself. I noticed that initWithFrame is called twice - I'm assuming it's for the parent window and the NSView?
What am I missing?THANKS!
This is an XCode generated Document based app using COCOA and Obj-C. I'm new at both.
You mention that initWithFrame: is called twice. Your initWithFrame: should only be called once (unless you happen to have two Canvas views).
Is it possible you have the Canvas view in your nib/xib file and are also creating another in code (with alloc/initWithFrame:)?
In which case you have two Canvas objects. You probably have one hooked up to your controls and the other one is in the window (and thus responding to the mouseUp: and it is giving you the same value every time).
If you have the Canvas view setup in IB, you can fix this problem by removing your code that is creating the second one.
You've probably run in to a special case: NSNumber could have cached instances to represent commonly-used numbers.
Two observations, though:
You're wasting a whole lot of memory using NSNumber when you could be simply using NSIntegers or maybe an old-fashioned enumerated type, completely avoiding the object overhead.
You never actually showed your code for when you look at the instances of NSNumber; without it, there's not really enough information here to answer your question.