Accessing utility methods from any class instance? - ios

(This has probably been answered elsewhere but I don't know what to search on to find it)
I have the printErrorMessage method below which I find very useful. I've been including it in all my classes but that is kind of dumb in terms of duplicating code. Can I just define this as a Class method in a separate Utility class?
This is on iOS, if that matters.
- (void) printErrorMessage: (NSString *) errorString withStatus: (OSStatus) result
{
char str[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(result);
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
str[0] = str[5] = '\'';
str[6] = '\0';
} else
// no, format it as an integer
sprintf(str, "%d", (int)result);
NSLog (#"*** %# error: %s\n", errorString, str);
}

Can I just define this as a Class method in a separate Utility class?
Of course you can. That is what class methods are for. Alternatively, inject it into some existing class by way of a category.

I use to create a class with static methods:
#interface Utils
+ (void) printErrorMessage: (NSString *) errorString withStatus: (OSStatus) result
#end
Now you can call this from anywhere in your code using:
[Utils printErrorMessage:#"string" withStatus:status];

Related

How to check whether a protocol contains certain method programatically in Objective-C?

Is there any way to check whether a protocol contains certain method or whether a method belongs to certain protocol in Objective-C?
I don't think the redirected question is the same as mine. What I want is:
[MyProtocol containsSelector:#selector(MySelector)];
Or
[MySelector isMethodOfProtocol:#protocol(MyProtocol)];
See the Objective-C runtime functions
Protocol *objc_getProtocol(const char *name)
struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount)
The documentation can, at the time of this writing, be found here.
If you know the name of the method, here is what you can do :
First set the delegate of the protocol.
Then, check if the method belongs to the protocol as this :
if ([something.delegate respondsToSelector:#selector(someMethodToCheck)])
Here is a small code snippet I am using right now (thanks to Avi's answer above):
- (BOOL)isSelector:(SEL)selector
ofProtocol:(Protocol *)protocol {
unsigned int outCount = 0;
struct objc_method_description *descriptions
= protocol_copyMethodDescriptionList(protocol,
YES,
YES,
&outCount);
for (unsigned int i = 0; i < outCount; ++i) {
if (descriptions[i].name == selector) {
free(descriptions);
return YES;
}
}
free(descriptions);
return NO;
}
You can move this to a category on NSObject too, if you use forwarding extensively.
Here is a function I found Apple using:
#import <objc/runtime.h>
BOOL MHFProtocolHasInstanceMethod(Protocol *protocol, SEL selector) {
struct objc_method_description desc;
desc = protocol_getMethodDescription(protocol, selector, NO, YES);
if(desc.name){
return YES;
}
desc = protocol_getMethodDescription(protocol, selector, YES, YES);
if(desc.name){
return YES;
}
return NO;
}
Use like this:
- (id)forwardingTargetForSelector:(SEL)aSelector{
if(MHFProtocolHasInstanceMethod(#protocol(UITableViewDelegate), aSelector)){
...

Is there any handy method to enumerate and invoke tests of a XCTestCase class in Xcode?

There is a class which provides a set of initXXX methods and some functional methods. It is handy to invoke all the functional tests at runtime when verifying the instance created by all these initXXX methods.
Just like this:
-(void) testEverythingWithInitXXX {
if ( test of self.tests not postfix by 'WithInit.*' ) {
invoke it.
}
}
It is always possible by inspecting the ObjC object at runtime, I'm just wondering if Apple provided any help already in XCTest framework?
-- update --
I just found something.
NSArray * tests = [FileLibraryTests testInvocations];
for (NSInvocation * inv in tests ) {
NSString * sel = NSStringFromSelector(inv.selector);
if (![sel containsString:#"WithInit"]) {
[inv invokeWithTarget:self];
}
}
You could try this (using runtime you can fetch all methods from your class)
void DumpObjcMethods(Class clz) {
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(clz, &methodCount);
printf("Found %d methods on '%s'\n", methodCount, class_getName(clz));
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL customSelector = NSSelectorFromString([NSString stringWithFormat:#"%s", class_getName(clz)]);
[self performSelector:customSelector withObject:nil];
/**
* Or do whatever you need here...
*/
}
free(methods);
}
if you want to perform only some special method - check prefix via NSString method
if ([methodStringRepresentation hasPrefix:#"init"])
{
// perform selector
}
Hope this helps

Objective-C isEmpty helper suddenly stopping build

I've got this beautiful & convenient helper inline function that i have in a project (originally has it's roots in here & here):
static inline BOOL isEmpty(id thing) {
return !thing
|| [thing isKindOfClass:[NSNull class]]
|| ([thing respondsToSelector:#selector(length)] && [((id)thing) length] == 0)
|| ([thing respondsToSelector:#selector(count)] && [((id)thing) count] == 0);
}
static inline BOOL isNotEmpty(id thing) {
return !isEmpty(thing);
}
and all works well.
it's useful for checking NSString, NSData, NSArray, NSDictionary, NSSet, and others... My issue now is that I brought it in to another project(a static framework/library that i'll be using) and have the following issue that is stopping my project from building:
I'm using the same(latest) version of xCode with both so not sure what the difference could be that would stop this on one side and not the other... The project settings are obviously different in either project (as mentioned, one is a framework and one is a regular project) but would that do it?
thanks in advance!
POST-SOLUTION-EDIT for future visits:
hold command and click on the method or property to get a drop down of all the instances that the compiler is seeing... you likely have conflicting return types.
It sounds like the problem is that some class(es) in the framework/library declares a -count method that returns something different than -[NSArray count] (etc.).
Even when you're sending a message to an object of unknown (id) type, the compiler needs to know some information about the method that will be called, including the return type. In short, this is because the message send path is different depending on the return type of the method that will be called. In cases like your code, the compiler will look for any method declared in the project with a name matching the message you're sending, and assume that's the method that will be called for the purposes of figuring out return type, etc. In this case, it's finding two different methods with the same name, but which have differing return types, and therefore doesn't know the exact semantics required for sending the count message.
The quick and dirty solution is to change the cast in your isEmpty() function to [(NSArray *)thing count]. This should work fine as long as you never call isEmpty() with instances of whatever class it is that has a different -count method.
update: changed the sense of the method to is not empty, to handle nil values too.
Not quite an answer, but you could do this with categories:
#interface NSObject (IsEmpty)
-(BOOL)isNotEmpty ;
#end
#implementation NSObject (IsEmpty)
-(BOOL)isNotEmpty { return NO ; /* I guess? */ }
#end
#implementation NSArray (IsEmpty)
-(BOOL)isNotEmpty { return self.count > 0 ; }
#end
#implementation NSDictionary (IsEmpty)
-(BOOL)isNotEmpty { return self.count > 0 ; }
#end
#implementation NSSet (IsEmpty)
-(BOOL)isNotEmpty { return self.count > 0 ; }
#end
#implementation NSNull (IsEmpty)
-(BOOL)isNotEmpty { return NO ; }
#end
Now you can do this:
id objectOrNil = ... ;
BOOL isEmpty = ![ objectOrNil isNotEmpty ] ;
I guess the proper way would be to explicitly cast the result of [thing count] to integer:
static inline BOOL isEmpty(id thing) {
return !thing
|| [thing isKindOfClass:[NSNull class]]
|| ([thing respondsToSelector:#selector(length)] && [((id)thing) length] == 0)
|| ([thing respondsToSelector:#selector(count)] && (NSUInteger)[((id)thing) count] == 0);
}
This will tell compiler that you expect to see unsigned integer as a result of this method invocation.
static inline BOOL isEmpty(id thing) {
return !thing
|| [thing isKindOfClass:[NSNull class]]
|| ([thing respondsToSelector:#selector(length)]
&& [(NSData *) thing length] == 0)
|| ([thing respondsToSelector:#selector(count)]
&& [(NSArray *) thing count] == 0);
}
It shouldn't confuse with [NSArray count] method. Hope that will help.

Expect Argument Type to be integer but getting id instead

I'm using the forwardInvocation: feature of objective-c and I need to know what type of argument the method received. In my example I'm passing it an int but getArgumentTypeAtIndex: tells me it's an id instead. Here's a simple example:
#interface Do : NSObject
+ (void) stuff:(int)x;
#end
#implementation Do
+ (NSMethodSignature *) methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature)
signature = [self methodSignatureForSelector:#selector(forwardInvocation:)];
return signature;
}
+ (void)forwardInvocation:(NSInvocation *)i
{
const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
NSLog(#"%s == %s", argType, #encode(id)); // # == #
NSLog(#"%s == %s", argType, #encode(int)); // # == i
}
#end
Here's how I call it:
[Do stuff:123];
Any idea why I'm not getting id instead of int as the type?
The problem is that you don't have actually have a stuff: method on the class so methodSignatureForSelector: will return nil - it looks like you discovered that and so implemented your own version, but that fails on the super call and so ends up returning the signature of forwardInvocation: - which is not what you want!
To get around this you either need to direct the methodSignatureForSelector: to a class which has the selector, or use a protocol - if a class implements a protocol then it will return the signature for any methods in that protocol even if the methods are not actually implemented by that class.
Here is your sample using a protocol:
#protocol DoProtocol
#optional
+ (void) stuff:(int)x;
#end
#interface Do : NSObject<DoProtocol>
#end
#implementation Do
+ (void)forwardInvocation:(NSInvocation *)i
{
const char* argType = [i.methodSignature getArgumentTypeAtIndex:2];
NSLog(#"%s == %s", argType, #encode(id)); // # == #
NSLog(#"%s == %s", argType, #encode(int)); // # == i
}
#end
The #optional avoids any compiler warnings for unimplemented methods. The default implementation of methodSignatureForSelector: (from NSObject) will return a valid signature obtained from the protocol, and so forwardInvocation: will be called.
As long as you can get it past the compiler, whatever you pass as an argument will be interpreted as such at runtime - you could declare that a function takes an NSNumber, but it you pass a UITableView to it, it's class will still be a UITableView.

Best way to enum NSString

Im digging for ways to enum objc object such as NSString, I remember there a new feature in a version of Xcode4+ which offering a new way to enum , but not clearly. Anyone know that?
OK, I answered myself. Guess I make a mistake.
This is the new feature I mentioned above:
typedef enum Language : NSUInteger{
ObjectiveC,
Java,
Ruby,
Python,
Erlang
}Language;
It's just a new syntax for enum in Xcode 4.4, but I'm so foolish to think we can exchange "NSUInteger" to "NSString".
So here is the way I found that works:
http://longweekendmobile.com/2010/12/01/not-so-nasty-enums-in-objective-c/
// Place this in your .h file, outside the #interface block
typedef enum {
JPG,
PNG,
GIF,
PVR
} kImageType;
#define kImageTypeArray #"JPEG", #"PNG", #"GIF", #"PowerVR", nil
...
// Place this in the .m file, inside the #implementation block
// A method to convert an enum to string
-(NSString*) imageTypeEnumToString:(kImageType)enumVal
{
NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray];
return [imageTypeArray objectAtIndex:enumVal];
}
// A method to retrieve the int value from the NSArray of NSStrings
-(kImageType) imageTypeStringToEnum:(NSString*)strVal
{
NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray];
NSUInteger n = [imageTypeArray indexOfObject:strVal];
if(n < 1) n = JPG;
return (kImageType) n;
}
FYI. The original author of the second example code created a category for enum handling. Just the thing for adding to your very own NSArray class definition.
#interface NSArray (EnumExtensions)
- (NSString*) stringWithEnum: (NSUInteger) enumVal;
- (NSUInteger) enumFromString: (NSString*) strVal default: (NSUInteger) def;
- (NSUInteger) enumFromString: (NSString*) strVal;
#end
#implementation NSArray (EnumExtensions)
- (NSString*) stringWithEnum: (NSUInteger) enumVal
{
return [self objectAtIndex:enumVal];
}
- (NSUInteger) enumFromString: (NSString*) strVal default: (NSUInteger) def
{
NSUInteger n = [self indexOfObject:strVal];
if(n == NSNotFound) n = def;
return n;
}
- (NSUInteger) enumFromString: (NSString*) strVal
{
return [self enumFromString:strVal default:0];
}
#end
Alternative way to use struct:
extern const struct AMPlayerStateReadable
{
__unsafe_unretained NSString *ready;
__unsafe_unretained NSString *completed;
__unsafe_unretained NSString *playing;
__unsafe_unretained NSString *paused;
__unsafe_unretained NSString *broken;
} AMPlayerState;
const struct AMPlayerStateReadable AMPlayerState =
{
.ready = #"READY",
.completed = #"COMPLETE",
.playing = #"PLAYING",
.paused = #"PAUSED",
.broken = #"BROKEN"
};
Then you can use like this:
NSString *status = AMPlayerState.ready;
Easy to use, readable.
Would be nice if someone update/edit answer with advantages/disadvantages of this approach.
Recommended way from apple docs:
You use the NS_TYPED_ENUM to group constants with a raw value type that you specify. Use NS_TYPED_ENUM for sets of constants that can't logically have values added in a Swift extension, and use NS_TYPED_EXTENSIBLE_ENUM for sets of constants that can be expanded in an extension.
Apple docs
typedef NSString *MyEnum NS_TYPED_ENUM;
extern MyEnum const MyEnumFirstValue;
extern MyEnum const MyEnumSecondValue;
extern MyEnum const MyEnumThirdValue;
in the .h file. Define your strings in the .m file
MyEnum const MyEnumFirstValue = #"MyEnumFirstValue"
MyEnum const MyEnumSecondValue = #"MyEnumSecondValue";
MyEnum const MyEnumThirdValue = #"MyEnumThirdValue";
Works as expected in both Objective-C
- (void)methodWithMyEnum:(MyEnum)myEnum { }
and Swift
func method(_ myEnum: MyEnum) { }
This will be validated by compiler, so you won't mix up indices accidentally.
NSDictionary *stateStrings =
#{
#(MCSessionStateNotConnected) : #"MCSessionStateNotConnected",
#(MCSessionStateConnecting) : #"MCSessionStateConnecting",
#(MCSessionStateConnected) : #"MCSessionStateConnected",
};
NSString *stateString = [stateStrings objectForKey:#(state)];
<nbsp;>
var stateStrings: [MCSessionState: String] = [
MCSessionState.NotConnected : "MCSessionState.NotConnected",
MCSessionState.Connecting : "MCSessionState.Connecting",
MCSessionState.Connected : "MCSessionState.Connected"
]
var stateString = stateStrings[MCSessionState.Connected]
UPDATE: A more Swifty way is to extend the enum with CustomStringConvertible conformance. Also, this way the compiler will safeguard to implement every new addition to the underlying enum (whereas using arrays does not), as switch statements must be exhaustive.
extension MCSessionState: CustomStringConvertible {
public var description: String {
switch self {
case .notConnected:
return "MCSessionState.notConnected"
case .connecting:
return "MCSessionState.connecting"
case .connected:
return "MCSessionState.connected"
#unknown default:
return "Unknown"
}
}
}
// You can use it like this.
var stateString = MCSessionState.connected.description
// Or this.
var stateString = "\(MCSessionState.connected)"
Update in 2017
Recent down votes drew my attention, and I'd like to add that enum is really easy to work with String now:
enum HTTPMethod: String {
case GET, POST, PUT
}
HTTPMethod.GET.rawValue == "GET" // it's true
Original Answer
Unfortunately I ended up using:
#define HLCSRestMethodGet #"GET"
#define HLCSRestMethodPost #"POST"
#define HLCSRestMethodPut #"PUT"
#define HLCSRestMethodDelete #"DELETE"
typedef NSString* HLCSRestMethod;
I know this is not what OP asked, but writing actual code to implement enum seems to be an overkill to me. I would consider enum as a language feature (from C) and if I have to write code, I would come up with some better classes that does more than enum does.
Update
Swift version seems to be prettier, although the performance can never be as good.
struct LRest {
enum HTTPMethod: String {
case Get = "GET"
case Put = "PUT"
case Post = "POST"
case Delete = "DELETE"
}
struct method {
static let get = HTTPMethod.Get
static let put = HTTPMethod.Put
static let post = HTTPMethod.Post
static let delete = HTTPMethod.Delete
}
}
I think you are looking for the inline array function. eg
#[#"stringone",#"stringtwo",#"stringthree"];
if not, i'm not sure you can enum objects.
you could however have a static array of strings and have the enum reference object at index.
This is how I do it, although it's not perfect. I feel the switch mechanism could be improved... also not positive about hash-collision resistance, don't know what apple uses under the hood.
#define ElementProperty NSString *
#define __ElementPropertiesList #[#"backgroundColor", #"scale", #"alpha"]
#define epBackgroundColor __ElementPropertiesList[0]
#define epScale __ElementPropertiesList[1]
#define epAlpha __ElementPropertiesList[2]
#define switchElementProperty(__ep) switch(__ep.hash)
#define caseElementProperty(__ep) case(__ep.hash)
-(void)setValue:(id)value forElementProperty:(ElementProperty)ep;
[self setValue:#(1.5) forElementProperty:epScale];
//Compiler unfortunately won't warn you if you are missing a case
switchElementProperty(myProperty) {
caseElementProperty(epBackgroundColor):
NSLog(#"bg");
break;
caseElementProperty(epScale):
NSLog(#"s");
break;
caseElementProperty(epAlpha):
NSLog(#"a");
break;
}

Resources