i'm trying to understand this in objective-c :
in this example, indexPath is a pointer but we use it "as is" in the function : indexPath.section, instead of (for example) *indexPath.section(with a *) :
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
return (indexPath.section == 0) ? nil : indexPath;
}
so, in objective-c, we don't need to add a * to get the content of the variable where the pointer points to...?
but i found this function, where they use a * on the pointer inside the function (on reverse ) :
NSInteger lastNameFirstNameSort(id person1, id person2, void *reverse)
{
NSString *name1 = [person1 valueForKey:LAST];
//...
if (*(BOOL *)reverse == YES) {
return 0 - comparison;
}
and for the id variables, they are using the variable name as is : for example here : person1
So, could someone explain me the differences between those 2 examples :
why on the first example, we don't add a * on indexPath,
why we don't add this * on the id variables, and we use it with *reverse in the second example?
Thanks
You are confusing dot-notation with reading a structure. This is not surprising, since Apple made them ambiguous.
indexPath.section does not mean "the section structure element in indexPath. It doesn't even mean "the property section in indexPath." It means [indexPath section]. It just calls the method section and returns the result.
Similarly, foo.bar = baz does not literally mean "set the property bar to baz." It literally means [foo setBar:baz]. Whatever setBar: does will be done. In most cases, it sets an ivar.
Since indexPath is also technically a struct pointer, it is possible in some cases (but not always, and not often if you code correctly) to say indexPath->section. You should never do this. (There are some extremely rare exceptions, but you are unlikely to encounter them.)
The frustrating thing about all of this is that foo.bar might be a structure reference or it might be a method call. Without knowing what foo is, you can't know. This is one of the problems with dot notation.
If you find it confusing, don't use dot notation (it continues to be a controversial feature among experienced developers). It is never required. It's just a shortcut for the more explicit [foo bar] and [foo setBar:baz].
*(BOOL *)result means "cast result from void* to BOOL * and then dereference it as a BOOL. It's unrelated to dot notation.
Dot-syntax on ObjC objects (pointers to objects) is a way of accessing methods of the forms [object getter] and [object setter: value], using java/c#/javascript like notation (object.property) While the syntax isn't exactly consistent, object->property is already taken by direct property access. reverse is just a normal void pointer, and so the (BOOL*) converts it to a BOOL pointer, and the * before it dereferences it. id types are still pointers, it's just that the syntax for property access in ObjC isn't consistent with the existing C syntax.
Related
I'm trying to cast from an id __autoreleasing * to a CFTypeRef * (void **).
I've tried:
id __autoreleasing *arg = [OCMArg setTo:mockData];
CFTypeRef expectedResult = (__bridge CFTypeRef) *arg;
[[[self.mockSecItemService expect] andReturnValue:OCMOCK_VALUE(mockCopyStatus)] copyItemMatching:queryCheck
result:&expectedResult];
But the code crashes when the autorelease pool is drained.
How does one convert to void** in an ARC environment?
I do not know the APIs you are using, so I'm not 100% sure of what's going on. I googled and they seem to be part of OCMock. I downloaded it and (without installing it as I'm not interested) I rapidly browsed the source.
I see something very fishy in that code. Here's how they implement the first method you call:
#implementation OCMArg
....
+ (id *)setTo:(id)value
{
return (id *)[[[OCMPassByRefSetter alloc] initWithValue:value] autorelease];
}
So they are returning an id* which is really just an id.
To me that's either a nonsense/error or an attempt to manipulate ObjC internals (even if undocumented, the first thing an ObjC object stores is in fact a pointer to the object class and is therefore of type Class which is compatible with id, therefore it somehow is valid to cast a pointer to an object or an id that refers to an object, to Class* or id*). I have no time or interest in going and studying the whole API to figure out why they do that. They may actually have a good reason (for example if you only pass that result to another API that knows what it's supposed to be, but you are doing more than that here). Instead of studying OCMock I'll try to explain you what is happening as far as I can say (ObjC and ARC).
id __autoreleasing *arg = [OCMArg setTo:mockData];
ARC will do absolutely nothing in this line of code.
What that method does you can see above. Class OCMPassByRefSetter is a simple class that just stores the argument after retaining it, so mockData is retained. The OCMPassByRefSetter is autoreleased and will disappear at the next drain (releasing the mockData and making *arg reference to released memory).
Note that arg in fact points to the isa of the OCMPassByRefSetter (the isa is the "first" ivar of any object, it's of type Class and points to the class of the object. But this is undocumented and may change at any time).
CFTypeRef expectedResult = (__bridge CFTypeRef) *arg;
*arg is of type id which is compatible with CFTypeRef, so the cast is valid. You use __bridge so ARC does absolutely nothing.
If arg pointed to a "toll free bridged" CF/Cocoa class this would be perfectly valid code but you'd have to be careful that expectedResult would become invalid at the next drain (it's not retained, but it's live as an autoreleased instance).
[[[self.mockSecItemService expect] andReturnValue:OCMOCK_VALUE(mockCopyStatus)] copyItemMatching:queryCheck
result:&expectedResult];
No idea what this line does. Given the prototype you posted in the comment above, ARC does nothing on the part result:&expectedResult.
You say it's a wrapper around SecItemCopyMatching, but as I understand it it's more than that. If it was just immediately calling SecItemCopyMatching passing it the result: argument, you'd likely be messing things up. But the name expectedResult and the fact that this is OCMock makes me think this is a little more complex than that.
You'll have to investigate it yourself a bit. But remember:
as soon as the current function exits, the argument you passed (&expectedResult) will become invalid as it's a local variable.
as soon as there is a drain, the value of expectedResult will become invalid, as that address points to memory that is going to be deallocated by the drain.
doing anything with the value of expectedResult is likely do be going very wrong as I do not think that a Class qualifies as "toll free bridged".
I suspect, but I may be very wrong, that you are not using the OCMock apis the way they are intended to be used. But on this front I cannot help you, and maybe you are actually doing it right.
Rather than try and figure out how to cast the variable into the correct format (OCMock is doing some complex things internally), I added another method, to handle the conversion.
- (OSStatus)findItemMatching:(NSDictionary *)query result:(id __autoreleasing *)outResult {
NSAssert(outResult, #"outResult is required");
CFTypeRef result = nil;
OSStatus status = [self copyItemMatching:query result:&result];
if (result) {
*outResult = CFBridgingRelease(result);
}
return status;
}
From my understanding both of the following getter methods reference the actual object.
So what is the difference between the two?
When and why would you want to use the second getter method?
- (MyObject *)myObject
{
return _myObject;
}
- (void)getMyObject:(MyObject **)myObject
{
if (!myObject)
{
*myObject = _myObject;
}
}
You would not use the second one.
Unless you like confusing people/yourself at a later date by not following the standard conventions.
It would make more sense if there was another piece of data that could also be returned for example look at NSManagedObjectContext
- (BOOL)save:(NSError **)error
The important result of the method is YES/NO did it save, but then we can also get an NSError object to inspect if there was an error.
In Objective C, an "object" is a C pointer, so an object value is actually already the same as a structure reference (an opaque structure with hidden fields if you want the code to be portable between Objective C runtimes).
So there is no "versus".
YouR first example is both.
There are special situations when an algorithm needs a reference to a reference, or a pointer to a pointer, but not very commonly. That would be your second example.
NSError objects are frequently used like this (taken from this previous question):
- (id)doStuff:(id)withAnotherObjc error:(NSError **)error;
I want to achieve something similar with BOOL indirection:
- (id)doStuff:(id)withAnotherObjc andExtraBoolResult:(BOOL **)extraBool;
But I can't figure out how to get this working correctly.
For the given method specification involving NSError, the proper implementation would involve something like (again from the previous question):
*error = [NSError errorWithDomain:...];
With similar logic, it seems like this should work with BOOL indirection:
*extraBool = &YES; // ERROR! Address expression must be an lvalue or a function designator
Why doesn't this work and what is the proper way to implement this?
Keep in mind that with objects, you're working with a pointer (e.g., NSError*), so using this method, you wind up with a pointer to a pointer (e.g., NSError**). When working with a BOOL, though, you should use a pointer to a BOOL: that is, only one level of indirection, not two. Therefore, you mean:
- (id)doStuff:(id)withAnotherObjc andExtraBoolResult:(BOOL *)extraBool;
and subsequently:
*extraBool = YES;
Does anyone know why I am getting the following error:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath;
{
id result = (Possession *)[possessions objectAtIndex:[indexPath row]];
[result setRowSwapped:TRUE]; //Passing argument makes pointer from integer without a cast
}
//This is the property on the possession object. I just want to set it as true thats it
#property (nonatomic) bool *rowSwapped;
TRUE is a number (equaling 1 in value) and setRowSwapped seems to expect a pointer of some kind as argument. Thus the compiler is transforming the int value 1 into a pointer, which is a warning, because this is hardly ever correct and if it was correct and you'd know what you are doing, you had used an explicit cast, which also avoids the warning. Sine you used no cast, you probably expected that the method expects a boolean as argument, and that seems to be incorrect.
BTW, since this is Objective-C, you should not use TRUE/FALSE in Objective-C, but YES/NO. The difference is that TRUE/FALSE is of type bool (all lower case, same as _Bool or boolean_t on Mac) and that again is usually of type int and 4 byte (at least on PPC and Intel, maybe not true for other Apple devices, like iPad). YES and NO are of type BOOL (all upper case) and this type is 4 Byte on PPC and 1 Byte on Intel. Thus bool and BOOL are not always the same and in some very rare conditions this can indeed cause problems. So you should not mix them. In Obj-C code use BOOL and YES/NO, in C code, use bool, boolean_t or _Bool and TRUE/FALSE.
You need to show us the definition of your setRowSwapped: routine to be sure, but it looks to me like you have a mismatch in the definition and the use. Did you make that method take a BOOL * parameter? Regardless, it's a little weird to type your variable id - what is your goal on that front?
I used bool instead of BOOL.
I assume according to "Cocoa design patterns" book i'm reading that the retain function is implemented using something like this :
- (int)retainCount
// Returns the receiver's current reference count
{
int result = 1; // receiver not in table, its count is 1
void *tableValue = NSMapGet(
[[self class] _myRefCountMapTable], self);
if(NULL != tableValue )
{ // if receiver is in table, its count is the value stored
result = (int)tableValue;
}
return result;
}
- (id)retain
// Increases the receiver's reference count
{
// store the increased value in the table
NSMapInsert([[self class] _myRefCountMapTable], self,
(void *)([self retainCount] + 1));
return self;
}
As the example imply every reference object has the same self member.
How does that happen ? maybe I don't understand the meaning of self - I though it's like "this" in C++.
If I just use assignment operator (A=B) Does it copy the pointer(self) and that's it ?
I though it would use "copywithzone" and it's relatives and the "self" members won't be equal.
Moreover, I though copywithzone is like copy constructor in c++.
I guess i'm confusing between the 2 worlds.
As the example imply every reference object …
There is no such thing as a “reference object”. I suspect that's not what you meant, so please clarify.
has the same self member.
Objects do not have members (instances have instance variables, which are similar in concept but not the same in implementation).
self is not a “member”, nor is it an instance variable. Note that classes have self as well. self is a special hidden argument to the message, containing the object that is the receiver of the message.
And no, self does not refer to every object at once. If you send the same message to two different objects, even of the same class, the self argument will contain a different pointer in each message.
maybe I don't understand the meaning of self - I though it's like "this" in C++.
As I understand “this”, yes. self is the object that received the message—in your examples, the object that something is retaining or asking the retain count of.
If I just use assignment operator (A=B) Does it copy the pointer(self) and that's it ?
The pointer copied will only be self if B is self. That is, if you say A = self, then it will copy the self pointer to A. If you say B = self and then you say A = B, same thing, since B and self contain the same pointer. If you had not said B = self, then B is probably some other value, so that other value is what will be copied to A. And that's assuming A and B are pointer variables.
It will copy the value (pointer) you tell it to copy. Nothing else.
I though it would use "copywithzone" and it's relatives and the "self" members won't be equal.
No. The object is only sent a copyWithZone: message (do not omit colons—they are significant) when something sends it a copyWithZone: message. The most common way is to send it a copy message, as that will send a copyWithZone: message in turn.
Furthermore, even a “copy” does not always copy the object. Immutable objects can implement copyWithZone: to return [self retain] instead.
However, plain assignment never copies the object. It only copies the pointer.
Moreover, I though copywithzone is like copy constructor in c++.
Roughly. I don't know enough C++ to say how much like it it is.
I remember having heard that you shouldn't make any assumptions on the retainCount. :-)
self is indeed very similar to this.
Assignment just copies the pointer, and it's the same in C++.
NSObject *objA =[[NSObject alloc] init];
NSObject *objB = objA;
objA and objB reference the same object.
Not that your code example uses [self class], so they'd use one table per class for all instances of that class.