Adding Objective-C method to class at runtime - objective-c

I'm trying to support newly added methods of NSColor in 10.9 on older systems. Pre-10.9 I had these in a category which allowed my to unify code between Mac and iOS. Now that 10.9 has these methods, I get odd drawing wherever I use them. I want to add these methods dynamically to older runtimes and I've found several references for how to do it using class_addMethod. The problem is, that even though addMethod returns success, the methods aren't called.
NSColor *
fColorWithWhite(id self, SEL _cmd, float white, float alpha) {
return [NSColor colorWithDeviceWhite: white
alpha: alpha];
}
NSColor *
fColorWithRedGreenBlue(id self, SEL _cmd, float red, float green, float blue, float alpha) {
return [NSColor colorWithDeviceRed: red
green: green
blue: blue
alpha: alpha];
}
+ (void)addLegacySupport {
Class class = NSClassFromString(#"NSColor");
BOOL success = class_addMethod(class, #selector(colorWithWhite:alpha:), (IMP)fColorWithWhite, "##:ff");
NSLog(#"colorWithWhite:alpha: - %i", success);
success = class_addMethod(class, #selector(colorWithRed:green:blue:alpha:), (IMP)fColorWithRedGreenBlue, "##:ffff");
NSLog(#"colorWithRed:green:blue:alpha: - %i", success);
}
Any pointers would be much appreciated.

You are trying to add class methods. You need to add them to the metaclass.
Class meta_cls = objc_getMetaClass("NSColor");

class_addMethod() adds an instance method to the class. You are trying to add a class method. Thus, you need to add the method to the metaclass (classes are instances of their metaclasses), which you can get by calling object_getClass() with the class as the argument:
Class metaclass = object_getClass(NSClassFromString(#"NSColor"));
BOOL success = class_addMethod(metaclass, #selector(colorWithWhite:alpha:), (IMP)fColorWithWhite, "##:ff");

Related

How to use Swift type nested in extension in objective-c?

I essentially want an enum of UIColors broken by which team would use them. The hope is that it could be accessed easily. Something like:
UIColor.Residential.white or UIColor.Commercial.white
The difficulty has been finding something that works in both Swift and Objective-C. Trying all manner of #objc and #objMembers in front of the extension and class declarations has stumped me. Is there a pattern for this kind of thing?
Something like the below would be ideal.
extension UIColor {
#objc class Residential {
static let navbarBlue = UIColor.color(hexString: "003865")
}
#objc class Commercial {
static let navbarBlue = UIColor.color(hexString: "010101")
}
}
Right now, I'm using the below, and it's working fine, but I dislike the naming duplication.
extension UIColor {
#objc static let residentialDarkBlueNavBar = UIColor.color(hexString: "003865")
#objc static let commercialLightBlueNavBar = UIColor.color(hexString: "0B60BB")
#objc static let residentialBackgroundNavBar = UIColor.color(hexString: "0E49AD")
#objc static let commercialBackgroundGray = UIColor.color(hexString: "F2F2F6")
}
Edit
The other question I've been referenced to recommends calling class methods to calculate the colors from a hex. But these are static variables that do not need to be calculated when called. Yes, the performance hit is negligible, but I'd like to access organized static variables in both Swift and ObjC without calling a method each time. If that's not possible, I'll do what the other question says. I just want to know if it is.
Thanks!
You can't use Swift enums in Objective-C unless they declare an integer raw type so to adhere to your specifications and be available between both languages it becomes convoluted to achieve what you're asking. You will have to create an enum for each set of colors containing the RGB Int value which is then used in a class function for each color type that only accepts colors within it's associated enum. Those functions then call a function to unpack the RGB value from the int which is then used to create and return the UIColor.
The requirement to keep the colors as a hex string and use enums between both languages isn't currently possible due interoperability restrictions of enums between Swift and Obj-C.
#objc public enum Residential: NSInteger {
case darkBlue = 14437
}
#objc public enum Commercial: NSInteger {
case lightBlue = 745659
}
extension UIColor {
#objc static func commercial(_ rgb: Commercial) -> UIColor {
return UIColor.rgb(rgb.rawValue)
}
#objc static func residential(_ rgb: Residential) -> UIColor {
return UIColor.rgb(rgb.rawValue)
}
#objc class func rgb(_ rgb: Int) -> UIColor {
let iBlue = rgb & 0xFF
let iGreen = (rgb >> 8) & 0xFF
let iRed = (rgb >> 16) & 0xFF
let iAlpha = (rgb >> 24) & 0xFF
return UIColor.init(red: CGFloat(iRed)/255, green: CGFloat(iGreen)/255,
blue: CGFloat(iBlue)/255, alpha: CGFloat(iAlpha)/255)
}
}
Useage:
let residentialBlue = UIColor.residential(.darkBlue) // Swift useage
UIColor *residentialBlue = [UIColor residential:ResidentialBlue]; // Obj-C useage
Edit:
The Int to RGB code used to create the UIColor was found and modified from this SO answer.
So the following works:
extension UIColor {
#objc(UIColorResidential) public class Residential: NSObject {
#objc static let navbarBlue = UIColor.color(hexString: "003865")
}
#objc(UIColorCommercial) public class Commercial: NSObject {
#objc static let navbarBlue = UIColor.color(hexString: "010101")
}
}
Don't forget to properly import them: #class UIColorResidential, UIColorCommercial; in .h and #import "YourModule-Swift.h" in .m

Objective C Extension equivalent to Swift Extensions?

What would be the equivalent of extensions in Objective-C as in Swift? Is it the same as creating a class function within a class?
extension CGRect{
static func rectWithTwoPoints(p1:CGPoint,p2:CGPoint) -> CGRect
{
return CGRectMake(min(p1.x, p2.x),min(p1.y, p2.y),fabs(p1.x - p2.x),fabs(p1.y - p2.y));
}
}
In objective C its category and in swift its extension
1.Click File -> New -> File
2.Select Objective-C file under Sources in iOS or Mac OS respectively and Click Next
3.Now select File Type as Category
Select UIView as baseclass of category and set name as "UIView+CGRect"
And you can add your methods like
UIView+CGRect.h of category :
+ (CGRect) rectWithTwoPoints:(CGPoint) p1 andWith:(CGPoint) p2;
UIView+CGRect.m of category :
+ (CGRect) rectWithTwoPoints:(CGPoint) p1 andWith:(CGPoint) p2 {
return CGRectMake(MIN(p1.x, p2.x), MIN(p1.y, p2.y), fabs(p1.x - p2.x), fabs(p1.y - p2.y));
}
And just import your category in view controller where you want to use it and access like
In ViewController.h
#import "UIView+CGRect.h"
And code will be
CGrect rect = [UIView rectWithTwoPoints:POINT_ONE andWith:rectWithTwoPoints:POINT_TWO];
You will get desired result.
There is no single equivalent, they're different languages with different capabilities.
For your example the 'equivalent' would be a utility function declared somewhere, likely just in a file, because CGRect isn't a class. It would be a C function, not an Obj-C method.
You could even declare a macro for it.
In Objective-C structures are not similar to classes, but plain C. Therefore they do not have functions associated with them. The usual pattern is that there are (C) functions dealing with the structure. You will find a bunch of them in the headers, for example:
CGRect CGRectMake ( CGFloat x, CGFloat y, CGFloat width, CGFloat height );
If you want to have an extra function, just write it:
CGRect rectWithTwoPoints(CGPoint p1, CGPoint p2)
{
return CGRectMake(min(p1.x, p2.x),min(p1.y, p2.y),fabs(p1.x - p2.x),fabs(p1.y - p2.y));
}
And put a prototype into a header, if you want to use it outside the defining compilation unit:
CGRect rectWithTwoPoints(CGPoint p1, CGPoint p2); // <- There is a semicolon for prototyping

How to document a struct within a struct in XCode (Swift or Obj-C)

We all know that when documenting a struct, you can use #struct for your struct and use #field to explains the content within the struct.
But how to document a struct with some fields that is within another struct?
In Swift, the example code will be:
struct Constants{
static let kButtonHeight : CGFloat = 0
struct Color {
static let kCloud: UIColor = UIColor(red: 236/255, green: 240/255, blue: 241/255, alpha: 1.0)
}
}
Thank you so much!
If you're doing this in Swift, you can prefix anything (type, property, or instance, etc.) with either single-line comments marked by /// or multi-line comments beginning with /**. So your documentation could simply be:
/// A global container for all constants
struct Constants{
/// The default button height
static let kButtonHeight : CGFloat = 0
/// Constant UIColor instances for my app
struct Color {
/// The cloud color - a very pale blue-white
static let kCloud: UIColor = UIColor(red: 236/255, green: 240/255, blue: 241/255, alpha: 1.0)
}
}
The documentation attaches immediately -- option click on a name and you get the relevant documentation:
I would define the Color struct outside (and before/above) the definition of Constants, and then the definition of constants would have a member of type Color, but without the 'nested' definitions. If you consider CGRect, this is a 'struct of structs' (a CGPoint and a CGSize) but those member structs are perfectly usable on their own as well..

Good practice for downcasting return type of inherited functions

I have got a Matrix-Class from which a Vector-Class is derived from and for extra functionality and better usage I've got a Vector3-Class which is derived from the Vector class. My problem is now that the Vector-class implements a function for instance +normalizeVector: which returns a new allocated Vector-instance. The subclass Vector3 should inherit these two functions but an inheritance leads to the function-prototypes that return a Vector-instance and not a Vector3-instance. This is just how inheritance works but is there a good practice how to solve that problem? A naive solution is to create Vector3 als a new class which subclasses NSObject but I want that Vector- and Vector3-instances can interact easily.
Here an code-example:
#interface Vector : NSObject {
....
}
+(Vector*) normalizeVector:(Vector*)v; //returns a new allocated Vector-instance
-(Vector*) normalize; //normalizes itself and returns itself
-(Vector*) otherFunction;
#end
#interface Vector3 : Vector {
}
-(Vector3*) specialFunction;
#end
usage of that code:
Vector3 *v3 = ...;
[[v3 normalize] specialFunction]; //Compiler gives me a warning because Vector has no specialFunction. Cast would help
[[Vector3 normalizeVector:v3] specialFunction]; //Compiler gives me a warning and during runtime it will crash because a `Vector` doesn't implement specialFunction
a cast to Vector3 would help but is not nice to work with and that also fails with the static function +normalizeVector: because in that static function a Vector-instance is allocated and a pointer-cast doesn't help.
any ideas? or other approaches / other modeling ?
edit: Code for my static function normalizeVector which gets inherited by Vector3:
#implementation Vector
...
+(Vector*) normalizeVector:(Vector *)v
{
unsigned int dim = vector_max(v.cols, v.rows);
Vector *res = [[[Vector alloc]initAsColumnVectorWithDim:dim] autorelease];
[Vector normalizeVector:v destination:res]; // this does only the logic: calc length and divide each component by the len and store at the vector passed to destination
return res;
}
#end
You will notice that -init methods always return type id -(id)init {..} exactly becauase of this.
Also, instead of [Vector alloc] - as you have noticed you don't actually know what Class you are in at runtime (it could be a subclass), so instead just use [self alloc] where self is the current Class because you are in a Class method. So, if you do [Vector3 normalizeVector:v] self is Vector3 and if you do [Vector normalizeVector:v] self is Vector.
Try adjusting your +normailzeVector: method to
+ (id)normalizeVector:(Vector *)v {
unsigned int dim = vector_max(v.cols, v.rows);
id res = [[[self alloc] initAsColumnVectorWithDim:dim] autorelease];
[self normalizeVector:v destination:res];
return res;
}
Just a note, + (id)normalizeVector: is not a function and definitely not a static function. It is a class method, it just helps to get the terms right.
In this case I would make normaliseVector an instance method. So instead of
Vector *newV = [Vector normalizeVector:v];
call
Vector *newV = [v normalizeVector];
Then you can produce a different normalizeVector for Vector and Vector3
EDIT:
For [[v3 normalize] specialFunction]; there is a problem in that normalize can sometimes return an object that specialFunction does not work on - ie it only works if v3 is a Vector3. So in this case there is extra information you have so a cast would be needed or that Vector3 normailze differs from Vector's. In this case I would produce a cover method on Vector3 to call normalize] specialFunction] so that the cast is in Vector3 specific code.

Objective-C Method Parameters Problem

I'm attempting to define an extremely simple utility method that will save me from having to use a calculator to define RGB values as percentages. When I look into Apple's sample code called "QuartzCache", in the DrawView.m file, line 96, I see this:
float whiteColor[4] = {1, 1, 1, 1};
However, when I attempt to created a method like the following, the compiler hates me. A half-hour of intensive Googling has not produced any help.
+(float[])percentagesRGBArray:(float[])rgbArray{
float red = rgbArray[0];
float green = rgbArray[1];
float blue = rgbArray[2];
float alpha = rgbArray[3];
red = red/255;
green = green/255;
blue = blue/255;
alpha = alpha;
float percentagesRGBArray[4] = {red, green, blue, alpha};
return percentagesRGBArray;
}
What is the proper way to define such a method? What am I doing wrong here?
Define a struct that contains all of the components, or wrap up each individual component in an NSNumber. Alternatively, use an NSColor instance to contain your colour components.
struct way:
typedef struct
{
float red;
float green;
float blue;
float alpha;
} MyColor;
- (MyColor) percentagesRGBArray:(MyColor) incoming
{
MyColor result;
result.red = incoming.red / 255;
result.green = incoming.green / 255;
result.blue = incoming.blue / 255;
result.alpha = incoming.alpha;
return result;
}
NSNumber way:
- (NSArray *) percentagesRGBArray:(float[]) rgbArray
{
NSNumber *red = [NSNumber numberWithFloat:rgbArray[0] / 255];
NSNumber *green = [NSNumber numberWithFloat:rgbArray[1] / 255];
NSNumber *blue = [NSNumber numberWithFloat:rgbArray[2] / 255];
NSNumber *alpha = [NSNumber numberWithFloat:rgbArray[3]];
return [NSArray arrayWithObjects:red, green, blue, alpha, nil];
}
NSColor way:
- (NSColor *) percentagesRGBArray:(float[]) rgbArray
{
CGFloat red = rgbArray[0] / 255;
CGFloat green = rgbArray[1] / 255;
CGFloat blue = rgbArray[2] / 255;
CGFloat alpha = rgbArray[3];
return [NSColor colorWithDeviceRed:red
green:green
blue:blue
alpha:alpha];
}
Normally, you would use Cocoa's NSColor class to handle this sort of thing, but it looks like you are doing something a little more low-level.
In that case, I would do the following:
typedef struct
{
float red;
float green;
float blue;
float alpha;
}
RGBAData;
RGBAData ConvertRGBAToPercentages(const RGBAData source)
{
RGBAData percentages;
percentages.red = source.red/255;
percentages.green = source.green/255;
percentages.blue = source.blue/255;
percentages.alpha = source.alpha/255;
return percentages;
}
To be used as follows:
RGBAData original = { 0xFF, 0xFF, 0x00, 0x80 }; // 50% transparent yellow
RGBAData percents = ConvertRGBAToPercentages(original);
Both e.James and dreamlax's answers give good approaches for doing this. But to answer what was wrong with your original code:
Basically, it has to do with how C arrays work. An array is essentially equivalent to a pointer to its first element. In fact, when you pass an array to a function, it decays into a pointer. You're still allowed to name the argument float myArray[4] (you have to declare the number of elements) just to make it clear that the pointer is supposed to be to an array of 4 elements — but you're still getting a pointer. Now consider the return value. What are you returning? We already established that you can't return an array by value, because it decays into a pointer. But even if you change the return type to be a pointer, it still won't work, because the array will have gone out of scope once the function returns. In order to return an array, you have to malloc the memory, and then you're responsible for freeing it later.
This is why you should avoid working with C arrays when at all possible. They're really low-level and fiddly. Even when you do use them, it's usually a good idea to hide them behind an API that takes care of the low-level details for you.
I think i'm late) but i have just found this thread.
the C way to do this is to create an array before invoking a function;
+(void) percentagesRGBArray:(float[])inArray toArray:(float*)outArray {
...
}
float array1[4];
float array2[4];
[MyClass percentagesRGBArray:array1 toArray:array2];