Is it possible to silence the warning on "performSelector" and receive the return value or object? - ios

I am familiar with the solutions to this ARC warning (performSelector may cause a leak because its selector is unknown) and have implemented them in most cases, but I can't seem to find a way to properly get the return value for a selector without just suppressing a warning.
It seems that maybe it can't or shouldn't be done, but a rewrite of code logic (developed by others) is too time consuming.
Code example:
NSString *message = [callback performSelector:validatorSel withObject:textCell.textField.text];

If validatorSel is known to not to begin with allocor new, or to have copy (or Copy) in its name, and you know there are no memory-management overrides involved (which are rare), then the default memory management will be correct here, and you can suppress the warning with an appropriate #pragma. If you cannot prove those things, then this may crash, which is why there's a warning.
If you cannot prove the above requirements, then there is no way to make this safe under ARC. You will either have to build it without ARC or rewrite it.

From your code sample it looks like you are expecting a selector for a method which takes an NSText * and return an NSString *. So from your linked answer you can determine that the implementation of this method has the function type:
NSString *(*)(ID, SEL, NSText *)
Here ID may be replaced by the type of callback, and NSText * can be replaced by the actual type of textCell.textField.text if our guess is wring.
Again from your linked answer, you can obtain the implementation and call it using:
NSString *(*implementation)(ID, SEL, NSText *)
= (void *)[callback methodForSelector: performSelector:validatorSel];
NSString *message = implementation(callback, validatorSel, textCell.textField.text);
As #RobNapier correctly points out this is only safe under ARC if the selector does not return a retained value, i.e. for normal[*] selectors if it is a member of the init, copy, or new method families. Now you are very unlikely to be passed an init family method for validatorSel as that would require callback to be a reference to an alloc'ed but not init'ed object, so we can ignore that one for now[#]. To test for the other two families you can use code along the lines of:
NSString *message; // for the return value of the selector
NSString *selName = NSStringFromSelector(validatorSel); // get string name of selector
if ([selName hasPrefix:#"new"] // starts with new,
|| [selName hasPrefix:#"copy"] // or copy,
|| [selName rangeOfString:#"Copy"].location != NSNotFound) // or contains "Copy"
{
// need to handle returning a retained object
...
}
else
{
// normal case
NSString *(*implementation)(ID, SEL, NSText *)
= (void *)[callback methodForSelector: performSelector:validatorSel];
message = implementation(callback, validatorSel, textCell.textField.text);
}
Which just leaves how to handle the return value correctly under ARC for copy and new family methods...
Handling copy and new family methods
ARC knows that a method, or function, returns a retained object by an attribute being placed on the method/function type. The naming convention is just the language's way of inferring the attribute if it is not present, it can be manually specified using the NS_RETURNS_RETAINED macro on a method/function declaration. So the missing code above is just:
{
// need to handle returning a retained object
NSString *(*implementation)(ID, SEL, NSText *) NS_RETURNS_RETAINED
= (void *)[callback methodForSelector: performSelector:validatorSel];
message = implementation(callback, validatorSel, textCell.textField.text);
}
The modified type for implementation tells ARC that it will return a retained object and ARC will handle the call the same way it does for known copy or new family methods.
HTH
Note: Handling init family methods
We skipped the init family not just because it is highly unlikely but also because it behaves differently - init family methods consume the object reference they are called on, that is they expect to be passed an owned object which they take ownership of, and will release it if needed. Unsurprisingly consuming an argument is also indicated by an attribute, just as for returning a retained object. The curious reader might wish to determine the code required, even though needing it is highly unlikely.
[*] A "normal" selector is one for a method which follows the standard naming conventions of Objective-C and does not use attributes to alter the memory ownership behaviour in ways contrary to the standard conventions. Only supporting standard conventions is not a big restriction, the whole point of the conventions is that code relies on them!
[#] You are of course very unlikely to be passed a new family selector as well, callback would usually have to be a reference to a class object, but handling it is the same as for the copy family so we've included it.

Related

Different result for objective-c syntax and C syntax

I have the following method section
- (NSString*) GetPathForFolder:(int)folder inDomains:(int) domains {
id cls = objc_getClass("NSFileManager");
SEL defaultManagerSelector = #selector(defaultManager);
SEL urlsForDirectoryInDomainsSelector = #selector(URLsForDirectory:inDomains:);
id fileMangerHandle = objc_msgSend(cls, defaultManagerSelector);
//NSArray<NSURL *>* notUsedArray = [fileMangerHandle URLsForDirectory:folder inDomains:domains];
NSArray<NSURL *>* resultArray = (NSArray<NSURL *>*) objc_msgSend(fileMangerHandle, urlsForDirectoryInDomainsSelector, folder, domains);
return [resultArray lastObject].absoluteString;
}
Calling this method with [self GetPathForFolder:5 inDomains:1]
returns file:///Applications/ which is wrong
The moment I uncomment the NSArray<NSURL *>* notUsedArray.. line I get a different and correct value which just happens to be the same as the one from
What does the objective-c version of the call do that I'm not doing in my C version?
Update:
I'm using objc_msgSend because this method will eventually be called from C# but it's just easier to first try it in objective-c and than start worrying about the interop part.
I was using sel_registerName because when running this inside of C#, I'm going to have to do my own registration.
More about Interop between C# and objective-c here. And also a java version of what I'm trying to understand is here.
You can’t use objc_msgSend like this, it is not a variadic function, even though it’s prototype suggests it. The compiler needs to use the same calling convention that the method is expecting. For this it needs to know the exact types of all parameters. You can tell it by casting objc_msgSend to a function pointer type with the correct parameters.
So for this case you would use something like this:
typedef NSArray<NSURL *> *(*UrlsForDirectoryType)(id, SEL, NSUInteger, NSUIteger);
NSArray<NSURL *>* resultArray = ((UrlsForDirectoryType)objc_msgSend)(fileMangerHandle, urlsForDirectoryInDomainsSelector, folder, domains);
The typedef is optional, of course, but then that makes the whole thing even harder to read.
To expose this to a different programming language you could also write regular C functions in Objective-C and call those. This is much easier to do than dealing with the runtime directly.

Clang nullability warnings and how to approach them

Recently I turned on CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION in Xcode and I am overwhelmed with nullability related warnings in my Objective-C code. The one warning type that is most prevalent is Implicit conversion from nullable pointer 'TypeA * _Nullable' to non-nullable pointer type 'TypeA * _Nonnull'.
I started my attempt to remove with these warnings by creating a local of the same type in a method as described here.
https://www.mail-archive.com/xcode-users%40lists.apple.com/msg02260.html
This article says by first using a local, that object is of attribute unspecified nullable, so it can be used as legit parameter to the methods expecting nonnull.
But I feel this is a cop out move and really not solving the issue in any beneficial way.
Has anyone gone through this exercise already? I would be grateful if you can share a strategy that you took.
Actually, I have messed around with that topic for a little bit. I wanted to improve nullability situation in a somewhat big project (make it more 'swiftier'). Here is what I found.
Firstly, you should turn on CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION (-Wnullable-to-nonnull-conversion)
Secondly, about
first using a local, that object is of attribute unspecified nullable,
so it can be used as legit parameter to the methods expecting nonnull.
This smells bad, and I created a macro called NONNUL_CAST(). Here is example how to implement it:
#define NONNUL_CAST(__var) ({ NSCAssert(__var, #"Variable is nil");\
(__typeof(*(__var))* _Nonnull)__var; })
Here you can see hacky __typeof(*(__var))* _Nonnull)__var, but it is not so bad. If __var is of type A* _Nullable we dereference __var, so it's type now just A, after we make reference again, but _Nonnull, and get nonnull __var as answer. Of course we assert to, in case something goes wrong.
Thirdly, you must specify nullabilty on every local variable, and you should put all your code in NS_ASSUME_NONNULL_BEGIN/END, like this:
NS_ASSUME_NONNULL_BEGIN
<your_code_goes_here>
NS_ASSUME_NONNULL_END`
You put there EVERY LINE (except imports) of your code, in .h and .m files. That will assume that all your arguments of methods, return types and properties are nonnull. If you want to make it nullable, put nullable there.
So, all done, now what?
Here is example of typical usage:
- (Atype*)makeAtypeWithBtype:(nullable BType*)btype {
Atype* _Nullable a = [btype makeAtype];
if (a) {
// All good here.
return NONNUL_CAST(a);
} else {
// a appeared as nil. Some fallback is needed.
[self reportError];
return [AtypeFactory makeDeafult];
}
}
Now you have more robust nullability situation. May be it is not looking nicely, but it is objective-c, so nothing to complain about.
Not every warning makes sense. Sometimes it's a shortcoming in the compiler. For instance, this code doesn't need a warning.
- (nullable id)transformedValue:(nullable id)value {
id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
return result;
}
We are checking to see if it's null! What more can we do? Why create an extra pointer?
So, we do this:
- (nullable id)transformedValue:(nullable id)value {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion"
id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
#pragma clang diagnostic pop
return result;
}
Why is this the proper answer?
First, it's OK to be smarter than the compiler. You don't want to start trashing your code, just because of a bogus warning.
This solution specifies the exact warning message to suppress, and it suppresses it for just one line.

Dynamically implementing a delegate during runtime

In my class, I have a reference on an UIViewController and want to implement a delegate on this ViewController during runtime. The delegate has only one method (with two parameters) and when the delegate-method on the ViewController is invoked, my class should handle the call.
I am quite sure this is possible with some kind of method swizzling, etc. but I don't know how to accomplish this.
What you want is possible, but it's not method swizzling, since you don't want to switch to methods but add a new one. It can be done, thanks to Objective-C's dynamic nature, but it's still a dirty hack so also file a feature request with the library vendor.
What you want is class_addMethod() and a C function with the actual implementation for that. One more thing, Objective-C methods are C methods, but with two implicit parameters, self and _cmd, which have to keep in mind (both when creating your C method and when telling class_addMethod your methods signature. And here is an SSCE of how to pull something like that off:
#import <Foundation/Foundation.h>
#import <objc/runtime.h> // Required for class_addMethod()
#interface MyClass : NSObject
#end
#implementation MyClass
#end
#protocol MyProtocol <NSObject>
- (void)printString:(NSString *)string;
#end
// Note the method signature containing the
// two implicit parameters self and _cmd!
void MyClassPrinStringIMP(id self, SEL _cmd, NSString *string)
{
NSLog(#"Hi I'm %#:%s and this is the string: %#", self, sel_getName(_cmd), string);
}
void PimpMyClass()
{
// The last argument is the signature. First character is the return type, in our case void
// Then comes self and _cmd, followed by the NSString. You can use #encode() to find out how your
// type is encoded. Best is to build this string at runtime, since the encoding can change with architectures
class_addMethod([MyClass class], #selector(printString:), (IMP)MyClassPrinStringIMP, "v#:#");
}
int main(int argc, const char * argv[])
{
#autoreleasepool
{
PimpMyClass();
id foo = [[MyClass alloc] init]; // id, to silence the compiler!
[foo printString:#"Hello World"];
}
return 0;
}
Example output:
Hi I'm <MyClass: 0x100101810>:printString: and this is the string: Hello World
Edit: Something that you may find is that the passed object is checked at runtime wether it conforms to a protocol or not using conformsToProtocol:. Since this code just adds the method implementation, it would still fail, but you can tell the runtime that you totally do implement that protocol with this one function call:
class_addProtocol([MyClass class], #protocol(MyProtocol));
Alternative: proxies
Objective-Cs dynamism and message forwarding is already praised by #JasperBlues, however, there is one particular class in Objective-C that is designed to do just that: NSProxy. It is designed to intercept sent messages and dispatching them dynamically to the relevant target, and does use the high-level NSInvocation approach. If you can pass a proxied object in some way as the delegate (depending on what your code allows for and what not), creating a NSProxy subclass might be the cleanest way to go.
However, note though that you then end up with a shim object that wraps over your other object, which comes with its own bag of pain and will break when you try to directly access variables via -> syntax. It's not a perfectly invisible proxy, but good enough for most cases.
Firstly, some comments indicate that what you're asking is instantly "a bad thing to do" or a "dirty hack". I disagree here. Most modern Object Oriented languages support these features, and they are used to good effect by numerous system-level frameworks. Of course it is human-nature to perhaps use these dynamic features where they're not really required (for fun or practice), even when a simpler approach would work fine. Beware of this.
Objective-C is admirable in that its somewhat of a legacy language and close to the "bare metal", and yet features a surprising level of dynamism, making it relatively easy to support these requirements without any external libraries or frameworks.
Besides using the class_addMethod guide that another answer correctly indicates, some other approaches are:
Message Forwarding: (recommended)
All NSObject sub-classes have the ability to forward a method that they're not able to respond to, to another target object. This is similar to the lower-level concept of trampolines. Apple publishes a guide on using this approach.
The advantages of using forward invocation is that it uses the NSInvocation level of abstraction, instead of directly calling the C ObjC runtime API. This abstracts the following details away:
Structs and primitives will be box/unboxed automatically
Dispatching to methods with a dynamic/unknown number of arguments becomes easy. Until arm64, this could be done using va_args, however on arm64 va_args can be copied directly to registers, and not popped off the stack.
Resolve Instance Method:
Instance methods are created by by registering a C function as the implementation to respond to a given message. This can be done neatly with blocks, using IMP_ImplementationWithBlock:
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
IMP imp = imp_implementationWithBlock((__bridge id) objc_unretainedPointer(
^(id me, BOOL firstParam, NSString* secondParam)
{
//Implementation goes in here
return something; //something of type 'id'
}));
class_addMethod(self, sel, imp, "##:");
return YES;
}
return NO;
}
Using libffi:
Libffi can also do this kind of thing, though it should not be necessary if you're using pure Objective-C, as the runtime already has these features baked in.

Determine if (void *) pointer from NSInvocation getArgument is object or primitive

Struggling with this one. Hoping it's possible and I don't sound silly.
I'm hacking forwardInvocation in a class I'm writing. What I want to do is forward the invocation to one selector or another depending on if it is an object or primitive type. The end goal is I want to "box" the primitives so they can be added to arrays/dictionaries. For simplicity, the two types of values that typically come through here are NSStrings and enums.
In short, given a pointer, is there a way to tell if it is an object?
__unsafe_unretained id argument;
[anInvocation getArgument:&argument atIndex:2];
// EXC_BAD_ACCESS if primitive (i.e. NSInteger value of 2 ($1 = 0x00000002) )
if (![argument isKindOfClass:[NSObject class]]) {
// Box the value
...
}
Is there a test I can run? Right now my code is hackishly doing this nasty trick:
// All my enums have at most 10 elements. I'm so bad at code.
if ((NSInteger)argument < 10) {
// Box the value
...
}
Thanks in advance.
You can get the type from the method signature:
NSMethodSignature *signature = [invocation methodSignature];
const char* argType = [signature getArgumentTypeAtIndex:2];
The types are listed under Type Encodings in the Objective-C Runtime Guide
You should also make sure you know the type before calling getArgument:atIndex::
This method copies the argument stored at index into the storage pointed to by buffer. The size of buffer must be large enough to accommodate the argument value.
__unsafe_unretained id argument;
[anInvocation getArgument:&argument atIndex:2];
This will write past argument on the stack if the size of the actual argument is greater than sizeof(id)
Pointers in C are just values that represent addresses. The thing at the address pointed to by a void pointer is explicitly untyped. If the goal is to have a method that can take any type — object pointer, scalar or composite — that just isn't going to work. And besides the impossibility of recovering a type from a void pointer, if you're literally trying to pass in a scalar directly instead of passing in its address, that is doubly impossible because the compiler needs to know right type of the value in order to emit the correct code, and most types cannot be converted to a pointer with any fidelity. Either way, Objective-C's type system is just not powerful enough to do what you want.

Fix warning: cannot pair a synthesized setter/getter with a user defined setter/getter

I use in my application QHTTPOperation.{h/m} found here all work properly but I got 8 warnings as follow:
Writable atomic property 'acceptableStatusCodes' cannot pair a
synthesized setter/getter with a user defined setter/getter
Writable atomic property 'acceptableContentTypes' cannot pair a
synthesized setter/getter with a user defined setter/getter
Writable atomic property 'authenticationDelegate' cannot pair a
synthesized setter/getter with a user defined setter/getter
...
I am asking because I have noticed that in the project of the above link there is no Warnings related to QHTTPOperation.{h/m}.
Any idea?
Thanks
declare the property nonatomic.
because the compiler does not verify a user-defined accessor's implementation is atomic or nonatomic, it assumes it is not atomic. this is a pretty safe assumption considering the actual implementation uses object level spin locks (in some cases), and the data which backs the implementation is abstracted from us. the only way we could fulfill the contact is by using the (private) runtime functions which the compiler uses, and then the compiler would have to verify the calls and parameters were correct in this scenario. thus, the user-defined accessor is not guaranteed to fulfill the standard objc runtime atomic contract.
Remove the #synthesize for these properties. They provided get/set.
EDIT: For clarity. In the .h, they declare acceptableStatusCodes with
NSIndexSet * _acceptableStatusCodes;
and
#property (copy, readwrite) NSIndexSet * acceptableStatusCodes;
Then, in the .m, they have
#synthesize acceptableStatusCodes = _acceptableStatusCodes;
and
- (NSIndexSet *)acceptableStatusCodes
{
return [[self->_acceptableStatusCodes retain] autorelease];
}
- (void)setAcceptableStatusCodes:(NSIndexSet *)newValue
{
if (self.state != kQRunLoopOperationStateInited) {
assert(NO);
} else {
if (newValue != self->_acceptableStatusCodes) {
[self willChangeValueForKey:#"acceptableStatusCodes"];
[self->_acceptableStatusCodes autorelease];
self->_acceptableStatusCodes = [newValue copy];
[self didChangeValueForKey:#"acceptableStatusCodes"];
}
}
}
These two blocks (the synthesize and the message implemenations) both define the same messages, so they are in conflict. The set message does an extra check at the beginning that the auto-generated synthesize will not do (the check for kQRunLoopOperationStateInited), so I would remove the synthesize, which is being ignored anyway.
The set message is correctly implementing copy semantics with
self->_acceptableStatusCodes = [newValue copy];
And it releases the old value. It also does keyValue change notification. I don't know why they left in the synthesize -- it looks like they might have wanted the state check later, and forgot to remove the auto-generated get/set.

Resources