What does id _Nullable __autoreleasing * _Nonnull mean? - objective-c

I am confused with the usage of (out id _Nullable *) in the code docs and the error my snippet throws at me.
#import <Foundation/Foundation.h>
void getvalue(char *a, bool *value){
#autoreleasepool {
NSURL *url = [[NSURL alloc] initFileURLWithFileSystemRepresentation:a
isDirectory:NO
relativeToURL:nil];
[url getResourceValue:value
forKey:NSURLIsDirectoryKey
error:nil];
}
}
void main (int argc, const char * argv[])
{
bool value[10];
char a[] = "a";
getvalue(a, value + 4);
}
Cannot initialize a parameter of type 'id _Nullable __autoreleasing * _Nonnull' with an lvalue of type 'bool *'
What does _Nullable * _Nonnull mean and what am I doing wrong in the code?
Reference:
getResourceValue:forKey:error:
- (BOOL)getResourceValue:(out id _Nullable *)value
forKey:(NSURLResourceKey)key
error:(out NSError * _Nullable *)error;

_Nullable * _Nonnull isn't the issue here. The problem is you're passing a pointer to a bool when you need to pass a pointer to a pointer to an object. getResourceValue:forKey:error: returns an object, not a primitive. In this case, an NSNumber.
NSNumber *result = nil;
[url getResourceValue:&result
forKey:NSURLIsDirectoryKey
error:nil];
*value = [result boolValue];
To the question, id _Nullable * _Nonnull means "a pointer, which must not be null, to an object-pointer, which may be null." This is the standard return-by-reference pattern in ObjC. id is a typedef for a pointer-to-an-object.

Related

Principle of Locking -- in Objective C

I got some problems with my codes that i can't tell.
Will u finger it out for me?
And can you help answer some of the following questions?
This class's functions may be called in multi-thread situation.
#import "FileHandler.h"
#import <pthread.h>
#define kJsonFilePath [NSTemporaryDirectory() stringByAppendingString:#"/foo/jsonFile.json"]
static pthread_mutex_t _lock;
#implementation FileHandler
+ (void)initialize
{
if (self == [YWMultiAccountsHandle class]) {
pthread_mutex_init(&_lock, NULL);
}
}
+ (void)writeIntoTheFileWithArray:(NSArray *)arr
{
pthread_mutex_lock(&_lock);
NSMutableArray *muArr = [NSMutableArray array];
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![obj isKindOfClass:[NSDictionary class]]) {
[muArr addObject:[obj get_keyValues]];
}
else
{
[muArr addObject:obj];
}
}];
pthread_mutex_unlock(&_lock);
NSData *data = [NSJSONSerialization dataWithJSONObject:[muArr copy] options:NSJSONWritingPrettyPrinted error:nil];
pthread_mutex_lock(&_lock);
[data writeToFile:kJsonFilePath atomically:YES];
pthread_mutex_unlock(&_lock);
}
1.Can i remove the pthread_mutex_unlock(&_lock); and pthread_mutex_lock(&_lock); in the middle?
2.Is there any problem in this class?
3.when the _lock will release and dealloc?
4.Is it thread safe?

objective c gets EXC_BAD_ACCESS error on completionhandler

i'm new to objective-c, please bear with me if i ask stupid questions :)
The following is part of code i have to start vpn tunnel, but keeps getting EXC_BAD_ACCESS error
- (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(BOOL * error))completionHandler {
vpnAdapter = [[OpenAdapter alloc] init];
vpnAdapter.delegate = self;
// get config
config = [[NSDictionary alloc] init];
NETunnelProviderProtocol *protocol = (NETunnelProviderProtocol *)self.protocolConfiguration;
config = protocol.providerConfiguration;
host = config[#"server"];
// Load config data
username = config[#"username"];
password = config[#"password"];
if(option != nil){
[vpnAdapter connect:host user:username pass:password add:YES completionHandler:^(BOOL success){
// return success;
completionHandler(&success); // Thread 2: EXC_BAD_ACCESS (code=1, address=0xbcc68f020)
}];
}else{
[vpnAdapter connect:host user:username pass:password add:NO completionHandler:^(BOOL success){
completionHandler(&success);
}];
}
}
here is connect method
- (void)connect: (NSString *) host user:(NSString *)username pass:(NSString *) password add:(Boolean) isAdd completionHandler:(void (^)(BOOL success)) completionHandler{
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0);
dispatch_queue_t connectQueue = dispatch_queue_create("me.ss-abramchuk.open-adapter.connection", attributes);
dispatch_async(connectQueue, ^{
// Call connect
//int ret=1;
NSArray* options = [NSArray arrayWithObjects:
#"--user", username,
host,
nil];
if(isAdd){
options = [NSArray arrayWithObjects:
#"--user", username,
#"--protocol", #"ad",
host,
nil];
}
//NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
//NSString *documentsDirectory = [paths objectAtIndex:0];
NSMutableArray *arguments = [NSMutableArray arrayWithCapacity:1+[options count]];
[arguments addObject:#"connect"];
[arguments addObjectsFromArray:options];
int argc = [arguments count];
char **argv = (char **)malloc(sizeof(char*) * (argc + 1));
[arguments enumerateObjectsUsingBlock:^(NSString *option, NSUInteger i, BOOL *stop) {
const char * c_string = [option UTF8String];
int length = (int)strlen(c_string);
char *c_string_copy = (char *) malloc(sizeof(char) * (length + 1));
strcpy(c_string_copy, c_string);
argv[i] = c_string_copy;
}];
argv[argc] = NULL;
const char *cfPass=[password UTF8String];
int ret = self.vpnClient->start2connect(argc, argv, cfPass);
BOOL result;
if (ret!=0){
result=false;
}
else {result = true;}
completionHandler(result);
});
}
all of these are from networkextension and while debugging, i found int ret = self.vpnClient->start2connect(argc, argv, cfPass);
seems not returning any value.
however, i confirmed that the start2connect method does return int value
so for now, anyone can help explain what's wrong?
thanks
The BOOL * is a pointer to a BOOL. We don’t use that pattern very often. We use it where the block needs to update a BOOL property somewhere, e.g. in enumerateMatchesinString, where you can update the boolean that stop points to in order to stop the enumeration.
But this is a completion handler, so there’s no point in passing a pointer to the boolean (one that presumably was on the stack, inviting problems). Just pass the boolean itself, not a pointer to it.
I would suggest that instead of:
- (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(BOOL * error))completionHandler {
...
[vpnAdapter connect:host user:username pass:password add:YES completionHandler:^(BOOL success){
completionHandler(&success);
}];
...
}
That you want:
- (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(BOOL success))completionHandler {
...
[vpnAdapter connect:host user:username pass:password add:YES completionHandler:^(BOOL success){
completionHandler(success);
}];
...
}
Note the block parameter isn’t BOOL * error but rather BOOL success and when it calls the completionHandler, there’s not & before success.
If there’s some reason you needed to update the BOOL, then that’s a different matter, but it doesn’t make sense in the context of a completion handler.

Objective C - Implement a method using an out id*

I am currently developing an Objective C class ClassA and I am trying to implement a method similar to NSURL method - (BOOL)getResourceValue:(out id __nullable * __nonnull)value forKey:(NSString *)key error:(out NSError ** __nullable)error that uses out id *:
My Method is: - (BOOL)changeString:(out id *)theString toString:(NSString *)newString
And I want my method to set theString to (NSString*) newString's value
I my method to be used like this:
id inputString;
[[ClassA alloc] changeString:&inputString toString:#"New String"];
inputString should now be set to "New String" (as id *)
How can I do this in - (BOOL)changeString:(out id *)theString toString:(NSString *)newString?
Thanks
This is pretty easy:
- (BOOL)changeString:(NSString * _Nonnull *)theString
toString:(NSString *)newString {
*theString = newString;
return YES;
}
And use it:
NSString *inputString;
BOOL result = [self changeString:&inputString toString:#"Hello World"];
NSLog(#"%d - %#", result, inputString);
inputString is not required to be nil, you can even pass a value
- (BOOL)changeString:(NSString * _Nonnull *)theString
toString:(NSString *)newString {
*theString = [NSString stringWithFormat: #"%# %#", *theString, newString];
return YES;
}
NSString *inputString = #"Hello";
BOOL result = [self changeString:&inputString toString:#"World"];
NSLog(#"%d - %#", result, inputString);

AFHttpClient invalid tokens in dictionary when using AFFormURLParameterEncoding

I've looked through SO and Google and haven't found a similiar issue to this. I feel like the answer is staring me in the face and I just need another set of eyes.
I'm using AFNetworking to connect to the Stripe.com API. Specifically I'm using AFHTTPClient postPath to send data to an endpoint, charges. Stripe requires the request to be encoded as application/x-www-form-urlencoded so I can't use JSON encoding.
The problem I'm running into is that I have a Charge object and a Card object. Card is a property on Charge and I convert both Charge and Card to NSDictionary's (Card is an dictionary inside of the Charge dictionary) and then pass them in as the parameters on the request like so:
NSDictionary *parameters = [ChargeRequest convertToDictionary:request];
[[StripeAPIClient sharedClient] postPath:#"charges" parameters:parameters
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Response: %#", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
NSLog(#"Response: %#", operation.responseString);
}];
When I do this, with AFHttpClient's parameterEncoding property set to AFFormURLParameterEncoding, Stripe returns this error:
"error": {
"message": "Invalid token id: {\n \"exp_month\" = 10;\n \"exp_year\" = 2016;\n number = 4242111111111111;\n}",
"type": "invalid_request_error"
}
The values in the error are specifically the key/values on the Card object after converting it. Here is the code I use for the conversion:
return [[NSDictionary alloc] initWithObjectsAndKeys:request.number, #"number", [NSNumber numberWithInt:10], #"exp_month", [NSNumber numberWithInt:2016], #"exp_year", nil];
Any advice on what do to get rid of the invalid tokens being put in this NSDictionary? Am I focusing on the wrong thing?
Thanks!
AFNetworking support only AFFormURLParameterEncoding with 1 level of parameters.
I'm writing a fix for that
replace AFQueryStringFromParametersWithEncoding implementation in AFHTTPClient by
extern NSArray * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey, NSStringEncoding encoding);
extern NSArray * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey, NSStringEncoding encoding);
extern NSArray * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey, NSStringEncoding encoding);
extern NSArray * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key, NSStringEncoding encoding);
NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding) {
NSMutableArray *mutableParameterComponents = [NSMutableArray array];
[mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersAtBaseKeyWithEncoding(parameters,nil,encoding)];
return [mutableParameterComponents componentsJoinedByString:#"&"];
}
NSArray * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey, NSStringEncoding encoding)
{
NSMutableArray *mutableParameterComponents = [NSMutableArray array];
if([parameters isKindOfClass:[NSDictionary class]]) {
[mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(parameters,baseKey,encoding)];
}
else if([parameters isKindOfClass:[NSArray class]]) {
[mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(parameters,baseKey,encoding)];
}
else {
[mutableParameterComponents addObject:AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(parameters,baseKey,encoding)];
}
return mutableParameterComponents;
}
NSArray * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey, NSStringEncoding encoding)
{
NSMutableArray *mutableParameterComponents = [NSMutableArray array];
id key = nil;
NSEnumerator *enumerator = [parameters keyEnumerator];
while ((key = [enumerator nextObject])) {
NSString *newKey = baseKey?[NSString stringWithFormat:#"%#[%#]",baseKey,key]:key;
[mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersAtBaseKeyWithEncoding([parameters valueForKey:key],newKey,encoding)];
}
return mutableParameterComponents;
}
NSArray * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey, NSStringEncoding encoding)
{
NSMutableArray *mutableParameterComponents = [NSMutableArray array];
for (id value in parameters) {
NSString* newKey = [NSString stringWithFormat:#"%#[]",newKey];
[mutableParameterComponents addObjectsFromArray:AFQueryParametersFromParametersAtBaseKeyWithEncoding(value,newKey,encoding)];
}
return mutableParameterComponents;
}
NSArray * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key, NSStringEncoding encoding)
{
return [NSString stringWithFormat:#"%#=%#", AFURLEncodedStringFromStringWithEncoding([key description], encoding), AFURLEncodedStringFromStringWithEncoding([parameter description], encoding)];
}
I'm just writing the same code for multipart requests and submit a pull request to AFNetworking

Getting errors in NSError code

Am getting errors in this piece of code; I have placed the error messages in the comments. Can't figure it out.
Thanks in advance.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSMutableString *str = [[NSMutableString alloc]init];
for (int i = 0; i < 10; i++) {
[str appendString:#"Aaron is cool!\n"];
}
// Declare a pointer to an NSError object, but don't instantiate it.
// The NSError instance will only be created if there is, in fact, an error.
NSError *error = nil;
// Pass the error pointer by reference to the NSString method
BOOL success =[str writeToFile:#"/tmp/cool.txt"; // Expected "]"
atomically:YES // Bad receiver type 'int'
encoding:NSUTF8StringEncoding
error:&error];
// Test the returned BOOL, and query the NSError if the write failed
if (success) {
NSLog(#"done writing /tmp/cool.txt");
} else {
NSLog(#"writing /tmp/cool/txt failed:#", error localizedDescription); // Expected ')'
}
}
return 0;
}
If your code doesn't have any typos this is the problem
// Pass the error pointer by reference to the NSString method
BOOL success =[str writeToFile:#"/tmp/cool.txt"; // Expected "]"
atomically:YES // Bad receiver type 'int'
encoding:NSUTF8StringEncoding
error:&error];
Remove the semicolon ";" from here.
BOOL success =[str writeToFile:#"/tmp/cool.txt"; // Expected "]"
try this:
#autoreleasepool {
NSMutableString *str = [[[NSMutableString alloc]init] autorelease];
for (int i = 0; i < 10; i++) {
[str appendString:#"Aaron is cool!\n"];
}
NSError *error = nil;
BOOL success =[str writeToFile:#"/tmp/cool.txt"
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
if (success) {
NSLog(#"done writing /tmp/cool.txt");
} else {
NSLog(#"writing /tmp/cool/txt failed: %#", [error localizedDescription]);
}
}
return 0;