enumerateKeysAndObjectsUsingBlock BOOL *stop [duplicate] - ios

This question already has an answer here:
What is the BOOL *stop argument for enumerateObjectsUsingBlock: used for?
(1 answer)
Closed 6 years ago.
I think it's funny about the api
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(KeyType key, ObjectType obj, BOOL *stop))block
I noticed the parameter stop.At first,I think it's a type of (BOOL *) I have never seen.But The document says
Discussion
If the block sets *stop to YES, the enumeration stops.
It seems like the parameter called *stop,and I delete * will get an error.
I wonder Why?

I think this is a very basic question about the C language on which Objective-C is based. You aren't familiar with pointers?
stop is a parameter of the block. Its type is BOOL* which means "a pointer to BOOL".
Somewhere within the implementation of -enumerateKeysAndObjectsUsingBlock: is code which calls the block you supllied. That code has created storage for a BOOL value. It is passing the pointer to (a.k.a. address of) that storage to your block so that your block may, by writing through the pointer, modify the value at that storage location.
The statement *stop = YES; assigns the value YES to the BOOL pointed to by the stop variable. That's what I meant above by "writing through the pointer". In this way, the block has modified a variable of the caller's, which is how the caller knows that the block wants the enumeration to stop.

Related

Proper updating of a 32bit method to 64bit support

I have a method I am trying to update to 64-bit support for my app. I have lots of these warnings.
The issue is with the below example. I get the warning:
Implicit conversion loses integer precision NSInteger to int
- (int) aMethod {
NSUserDefaults *u = [NSUserDefaults standardUserDefaults];
return [u integerForKey:#"someKey"];
}
If I change the return type to NSInteger I get
Conflicting return type in implementation of aMethod int vs NSInteger aka long
-(NSInteger) aMethod {
NSUserDefaults *u = [NSUserDefaults standardUserDefaults];
return [u integerForKey:#"someKey"];
}
I have tried casting the return type
return (NSInteger)[u integerForKey:#"someKey"];
or
return (long)[u integerForKey:#"someKey"];
But I can't get the error to go away unless I cast it to (int)
return (int)[u integerForKey:#"someKey"];
I have read many SO questions but I can't seem to get a good answer.
What is the proper way of updating this method?
The method:
- (int) aMethod ...
returns a value of type int. The method may first declared, probably in a header (.h) file, and then defined in an implementation (.m) file.
The statement:
return [u integerForKey:#"someKey"];
returns the result of calling integerForKey:, which is a value of type NSInteger.
NSInteger is not a type defined by the (Objective-)C language, rather it is a type alias (typedef) defined in the frameworks intended to represent the "natural" integer size on the platform. It is an alias for int on 32-bit platforms and long on 64-bit.
So your compiler error is telling you that the (now) 64-bit value is being truncated to fit a 32-bit result. There is no single right way to fix this, it depends on what the use of the value associated with #someKey is:
If the value should now be 64-bit then change the return type of aMethod; both in its separate declaration, if it has one, and in its implementation; to NSInteger. You will also need to check that wherever the method is called that returning an NSInteger value is acceptable, and you may need to make follow-on changes to variable types etc. You also need to look at places where the value associated with the key is set.
If the value associated with the key may remain as 32-bit then you can use intForKey: instead of integerForKey and leave the return type as is. You should also change where the key is set to store an int
HTH
Correction
There is (no longer?) a convenience method intForKey: on NSUserDefaults. Alternatives include: integerForKey: and casting; objectForKey: to obtain an NSNumber reference and then intValue; etc.

Best practice to declare Objective-C blocks as variable

I have a question regarding the best practice for declaring a block as a variable.
Initially I wrote my block variable like this:
id actionHandler = ^(UIAlertAction * action) {
// Handling code
};
To be later used like so:
UIAlertAction *action = [UIAlertAction actionWithTitle:#"Title"
style:UIAlertActionStyleDefault
handler:actionHandler];
But when I came across Apple's Working With Blocks guide, I saw I could rewrite it like so:
void (^actionHandler)(UIAlertAction * action) = ^(UIAlertAction * action) {
// Handling code
};
Is this the 'correct' way to declare it? That is in my opinion not as readable, but I don't have a lot of experience with Objective-C. So what is the best practice for declaring a block as a variable?
Edit: Alright, thanks all for the clarification! Defining a typedef as shown by amin-negm-awad and others seems like a good alternative approach as well.
There is no one-fits-all answer here: when you declare your block variable as id you no longer have compile-time information associated with your block, so calling it manually becomes problematic:
id myHandler = ^(NSString *str) {
NSLog(#"%#", str);
};
// Error: Called object type id is not a function or function pointer
myHandler(#"Hello");
if you want to make a direct call to the block from your code, you need to cast it back to a block.
On the other hand, if you declare a block variable only so that you could pass it to a function that takes a block as a parameter, using id provides a more readable approach.
Additional to the problem mentioned by dasblinkenlicht I want to ask a rhetoric question:
Likely you know that you can substitute this code …:
NSString *string = #"All about types";
… with this code:
id string = #"All about types";
Would you do? I'm sure, you don't.
So why should one change the "typed" version of the var into an id version? The only reason is, that the syntax of block types is unhandy and not easy to read (and not easy to write). I always define a concrete type to get rid of the unhandy syntax:
typedef void (^ActionHandlerType)(UIAlertAction * action);
And then:
ActionHandlerType actionHandler = ^(UIAlertAction * action) {
// Handling code
};
To make that clear: id is great to use the dynamic nature of Objective-C's message passing. But block execution is neither late bound. Nor the parameters of the block can change its number or type, so there is nothing to dynamically bind. It is a simple call with fixed numbers of arguments, fixed typed. Therefore the usage of id is possible as a side-effect of the block's object nature. But it is not an usage, which is intended.
BTW: If you use a concrete type in a parameter list, Xcode can autocomplete the syntax of the argument. With id this is not possible. Obviously.
If you use id in this context the compiler will not check that the type of the block you declare matches the type of the block the method expects. If you accidentally get the block wrong nasty, hard to debug, things will probably happen when the method tries to use the block...
So if you never make mistakes go with id, but if like me you do provide the correct type so the compiler can help you out when you do.
To make it easier, and consequently less error prone, use a typedef, e.g.:
typedef void (^AlertActionHandler)(UIAlertAction * action);
...
AlertActionHandler actionHandler = ^(UIAlertAction * action) { ...

NSError object already populated on first method call

I am following the book Test-Driven iOS development by G. Lee and came across this unit test, which I don't understand. First of all, if you need more code, please let me know right away.
-(void)testDelegateNotifiedOfErrorWhenNewsBuilderFails
{
MockNewsBuilder *builder = [MockNewsBuilder new];
builder.arrayToReturn = nil;
builder.errorToSet = underlyingError;
newsManager.newsBuilder = builder;
[newsManager receivedNewsJSON:#"Fake Json"];
...
}
-(void)receivedNewsJSON:(NSString *)objectNotation
{
NSError *error = nil;
// As you see error is nil and I am passing in a nil error.
NSArray *news = [_newsBuilder newsFromJSON:objectNotation error:&error];
...
}
#implementation MockNewsBuilder
-(NSArray *)newsFromJSON:(NSString *)objectNotation error:(NSError **)error
{
// But once I arrive here, error is no longer nil.
// (NSError **) error = 0x00007fff5cb887f0 domain: #"Fake Json" - code: 0
...
}
How is error auto-magically set?
UPDATE:
Thanks everyone for active discussion and advice. The answers explain how the caller side gets the error instance because of &, I understand that clearly. My question remains though why the callee side is pointing to a populated NSError instance, even though it had to be nil. I didn't set the error instance within newsFromJSON:error: so how is it already populated there?
I just changed [newsManager receivedNewsJSON:#"Fake Json1"]; and the error instance within newsFromJSON:error: reflects right away
(NSError **) error = 0x00007fff5b9b27f0 domain: #"Fake Json1" - code: 0. Its very confusing...
This is just pointer to pointer concept. You are passing the reference to the reference error object &error to the method -(NSArray *)newsFromJSON:(NSString *)objectNotation error:(NSError **)error;
And this will update the error object at the memory pointer you have passed.
See this is the concept of pointer to pointer.
Update:
Your error object is nil, yes its right. But you are not passing that error object to the newsFromJSON method, but the memory address of the error object( &error). That is the memory address of the error object.
This why you are getting non null value there inside your newsFromJSON method.
And one more thing, you can access the original object in your newsFromJSON method using the content of operator(* operator)
like **error = something;
This will update your original object ( NSError *error ) you declared in your caller method.
In C or CPP or Objective-C, & is the Address of operator and * is the content of operator.
&obj -> give the memory address of the obj
*obj -> give the content of the memory address in the obj.
** is a pointer to a pointer.
It means you need to pass a pointer address to a function or method.
Objective-C is a strict superset of C.
That means as in C functions and methods can only return one value.
There are two ways about it.
One is to wrap all your returns in structs or NSDictionaries or other collections.
This way is called an outParameter
It's passing a pointer address in.
C is a by copy language. But pointers are portable black holes that allow you to do wild things in C.
Objective-C and C++ give you the same wildness.
The error is set by Apple's framework code.
The Cocoa pattern is usually to return a BOOL and pass in an NSError pointer address.
If BOOL is NO check the NSError.
Apple framework will have put some presents in your NSError pointer address box.
Sometimes they don't use BOOL and instead return an object or nil.
Core Foundation C frameworks work very similarly and use in and out parameters a lot.
error is a variable of type NSError*, that is "pointer to NSError" (in objective-C, all objects are handled as references, as opposed to e.g. C++).
What this means is that error is a (local) variable that stores the address of the actual NSError object, initially nil.
The method you call creates an (autoreleased) NSError instance. In order to get a reference to that instance back, you need to pass the method the address of the pointer, or &error, which is, in turn, of type "pointer to pointer to NSError" (note the two-level indirection).
You do this because arguments to functions in C and methods in Objective-C are passed by value: if you just passed error, the value stored there (nil) alone is copied, and no matter what the called method does, the contents of the variable error on your side (the caller) can't be modified. To achieve this, you need to pass the address of error, or &error.
This way, the called method can "change" the contents of error (the address held there) so that it points to the newly created, NSError instance.
Does it make sense?
ADDENDUM: This is a very common pattern very often seen in Cocoa: The method being called could potentially fail, and instead of just using the return value to signal success/failure, and additional 'in/out' parameter is passed to retrieve detailed error information in case of failure. On failure, the method can return false (NO, 0, etc.), but in addition in can provide a more detailed error report (e.g. the reason for failure) inside the NSError instance.
EDITED: As #Droppy said, and seeing that all code involved is your own (i.e., not some first or third party framework), it is impossible that error is set to anything other than nil unless you explicitly allocate it somewhere. Perhaps you should "watch" it in the debugger to see when/where it is set. since the message seems to be set to #"Fake JSON", the first thing you could do is search that string in your project (all files).

Immutable Array/Dictionary Initialization [duplicate]

This question already has answers here:
How to fill NSArray in compile time?
(4 answers)
Closed 8 years ago.
If I initialize an NSArray as follows:
- (void) myMethod
{
NSArray *array = [NSArray arrayWithObjects:#"A","B","C",nil];
// DO SOME STUFF HERE
return;
}
Is this immutable array initialized at compile time or at run time? Same with NSDictionary. I'm thinking it must be at compile time but just wondering.
No… It would not be created in compile time… It would be created in runtime when the Method is being called.
By Immutability, it means that You cannot edit size of the array after once initializing it.
refer to this Link for more info about Object Mutability… Let me know if more info needed.. :)
Following code is what you want?
static NSString *strArray[] = {#"A", #"B", #"C"};
To use it with index like this: stringArray[index];
No, Method call arrayWithObjects: is not possible at compile time. During compilation, it just check that syntax, unless that variable would be constants or statics. Macros and constants are initialized at compile time. See this example below.
You could initialize directly, if the size is known as below(same example also posted by #simalone)
int num[5] = {2,8,7,6,0};

Block is preventing dealloc although the block was copied

I believe I was following the rules but still a problem exists
My class init includes a block like this:
HTTPChunkReceiveBlock chunkBlock = ^(id connection, NSData *data) {
NSLog(#"Hi there!!");
};
and I am passing this block into an HttpConn obj which my class holds:
operation_ = [[HttpClient sharedClient] performChunkedRequest:url
chunkHandler:chunkBlock];
Now for the problem: my object is never deallocated!!
The problem seems to be caused because the HttpConn is keeping a pointer to the block, but I want to mention two points:
The block is not referring to self!
The HttpConn class is keeping a copy of the block, like this:
chunkBlock_ = [chunkBlock copy];
Any explanation would be greatly appreciated!
EDIT
Extra info:
I have verified that if I'm freeing operation_ then my object is deallocated fine:
reader.operation_ = nil;
reader = nil; //previous line allows 'dealloc' to be called
Now repeating the question: operation did not get a pointer of reader's self, it only holds a copy of the block which do not refer to self!
Ok, I will answer my own question so that others do not fall into the same problem. #DarkDust was actually correct. there was a tiny line which I was completely ignoring:
**retriesNumber++;**
It looks like an innocent sentence, but because retriesNumber is a member of the class, it is actually meaning
(INVISIBLE strong pointer to self)->retriesNumber
so the solution was to declare it as a property (versus a member ivar) so that we can use self to access it, and then write:
pSelf->retriesNumber++;
Thank you guys for your quick support, and I hope others will learn from it too!

Resources