How can I assign a Swift struct by reference? - swift

As we know, in Swift, classes are reference objects whereas structs other data-types are value types. I'm trying to get the reference of a CGRect into a variable so I can change its value with a shorter name. Is there a way to achieve this? Attempting with an Objective-C or C++ pointer syntax is of no use here :(
let reference = someView.frame
frame = ...
If I stay at the view level it's ok because it's a reference type, but I want to include the frame in the reference.

You probably don't really want to work with references... I could be wrong, and you might have a good reason for wanting a reference. But looking at what you are trying to do, this might be a better approach:
Assign the frame that you want to work with to a variable:
var workingFrame = someView.frame
Work with the copy, making any changes that you want to make:
workingFrame = someNewRect
Update someView.frame to the new value:
someView.frame = workingFrame
There is, technically, a way to deal directly with pointers to memory addresses, but unless you have an amazingly good reason for going there, I think that most people would recommend that you avoid it.
[Edit:]
If you really want to try to work with pointers to memory addresses, then you may want to look at UnsafePointer<T>, UnsafeMutablePointer<T>, unsafeBitCast: and unsafeAddressOf:. Those types and functions will give you pointers to a struct.
For example, you can get a mutable pointer to an Int value like this:
let x = 5
let ptr: UnsafeMutablePointer<Int> = UnsafeMutablePointer(unsafeAddressOf(x))
Working with values, pointers, and memory addresses this way is discouraged, but yes, it is possible.
However, using unsafeAddressOf converts the Int to a class, so even that isn't really a pointer to the original struct. You may end up needing to initialize an UnsafeMutablePointer, allocate memory for it, and then assign a value to that memory. Then you can perform operations on the data at that memory address. Check out the documentation for UnsafeMutablePointer here.
And if you can give any more detail as to what, precisely, you are trying to do, there may be a more elegant solution. Swift does not make it easy to work with pointers, but it often provides other tools that allow you to accomplish what you need in a different way.

Here's the solution:
func pointerTo<T>(inout object: T) -> UnsafeMutablePointer<T> {
return withUnsafeMutablePointer(&object) {UnsafeMutablePointer<T>($0)}
}

Related

passing arguments by a button's tag in objective-c

I saw someone passing arguments by a button's tag as follow.
how could this work? and is it safe to do like this ?
NSCustomClass* cls = [[NSCustomClass alloc] init:#"",#"",#""];
[button setTag:(int) cls];
- (void)OnClick:(id)sender
{
NSCustomClass* cls = (NSCustomClass*)[sender tag];
// to do something with the "cls".
[cls release];
}
In fact,I didn't get weird results.
It works fine by passing arguments like 'NSString','NSArray' in this way.
Can you tell me why it is a bad idea?
Casting a custom class to an int is a very bad idea and you'll get weird results.
The tag is an integer property on all UI elements. It is declared as such on UIView:
#property(nonatomic) NSInteger tag;
You can assign any integer value to it, including any predefined constants:
#define Button1Constant 1
#define PushButtonConstant 2
// …Code
[button setTag:PushButtonConstant];
// …More code
if (sender.tag == PushButtonContent)
// Handle
In general you never want to abuse the frameworks. The tag is intended to store an integer and is used mainly to access a view with viewWithTag:, which can be useful in some cases if your view was built in Interface Builder but a referencing IBOutlet is inappropriate. Stuffing a pointer into an int can give unpredictable results, as others have indicated.
On top of that there's a memory management issue. Even if the pointer survives the cast you aren't retaining the object being pointed to. This in effect is a weak pointer but without any management by the run-time. Attempts to use the pointer will likely lead to the dreaded EXC_BAD_ACCESS. And who knows what ARC might do with this mess - blow up, I would expect.
To attach data to a button (which in and of itself sounds like a possible design flaw) you should either subclass or leverage the power of the run-time with objc_setAssociatedObject() and friends.
In general, casting from a pointer type to an integer type usually indicates a design flaw; there are very few legitimate reasons to do this, and I would strongly discourage doing it, especially in this case. It may work, but it's not guaranteed to work, simply because the tag property is not documented to be used this way. The more “hacks” that you put in your code, the harder it is to maintain.
Don't learn from the code where you saw this, instead stick to reputable tutorials and books.
As a side note, it is conventional in Objective-C to name all methods starting with a lowercase letter, so the method should be onClick: and not OnClick:. The exception to this rule is when the method starts with an acronym, for example -[NSString UTF8String].
You could subclass from UIButton and define a property by yourself, instead of using its tag property.

String compare vs Class compare in objective-C

I'm writing an objective-C game and I'm at the stage where i should start optimising some of my code in the game loops.
I have an extensive amount of class compare methods used,
if ([obj isMemberOfClass:[SomeClass class]])
etc.
I heard this sort of checking is quite expensive, because I choose only to have 1 array populated with multiple classes, I need some sort of class check.
I was thinking perhaps that adding a property to NSObject subclassing NSObject to contain a string property, that during initialisation i would make equal to the class name of that particular subclass. Then simply doing a
if ([obj.klass isEqualTo:#"SomeClass"])
Would this be beneficial?
I'm trying to keep as much dynamic coding out of the game loops as possible.
Thanks!
Short answer: no. String comparison is prohibitively more expensive compared to other methods of comparing (or: classifying, categorizing) objects.
Long answer: don't optimize what you haven't analyzed, measured and compared. What you really want to do before you start optimizing is to get a clear picture of how your app behaves and what its performance bottlenecks are. The change you're attempting is unlikely to lead to any noticeable change in performance, so I suggest to first find the real bottlenecks.
In this particular case, sending isEqual to an NSString is 4 times slower than isMemberOfClass if the test fails. And such conditional tests fail most of the time, which is why you should ignore the results of the test succeeding.
The successful string comparison is fast because it's a simple pointer comparison, if two strings are equal it is likely that they point to the same memory address. If they're not equal, then each character in the string will be compared for equality, probably by using the hash method.
Here are the results of the Object Comparison tests that I added to my performance test project. You can use that project to make further tests.
This is not really a direct answer to your question but is an answer in a broader sense.
In Objective-C the philosophy is more like that of Smalltalk in which you send the message and let the object decide what to do with it. If you find yourself having to do lots of tests to see what class an object is, you need to rethink your design.
For instance, if you have an array of objects and you want to convert each one to an integer to do some maths on it, you can do something like this:
for (id anObj in array)
{
int anInt = [anObj intValue];
// do something with anInt
}
It doesn't matter what the class of each anObj is, you can send -intValue to it. If your array is full of NSStrings and NSNumbers, for example, it doesn't matter, the code above will do what you expect.
Many classes do not define a method for the selector -intValue. For example, if you send that message to an instance of NSData it will respond by throwing an exception. There are a couple of ways to resolve this depending on circumstances.
ignore objects that don't respond to the selector by testing to see if the object knows about the selector
for (id anObj in array)
{
if ([anObject respondsToSelector: #selector(intValue)])
{
int anInt = [anObj intValue];
// do something with anInt
}
}
Define the selector for all classes you know will be put in the array. This is done by declaring a category. This way you can extend any Objective-C class without subclassing. For instance, you can define an intValue method for NSData that returns its length, or the sum of its bytes or some other appropriate value.
Formalise the requirement by declaring a protocol. You can then test for conformance to the protocol, or rely on compile time checks to make sure the objects you put in the array conform to the protocol.
There are lots of things you can do, but you need to get away a bit from the C++/Java model of class hierarchies. Objective-C is much more flexible in that respect.

Malloc() creates space for single struct, not array of structs

I've been banging my head against this problem all day, I would be very grateful to anyone who could help out.
Here's the deal - I'm trying to create a dynamic C array using malloc(). This array will hold CGPoint structs, which I start building and assigning right after the array is built. Here's the code:
CGPoint* tempVertices = malloc(sizeof(CGPoint) * 4); //defining a collision frame
tempVertices[0] = CGPointMake(37, 46);
tempVertices[1] = CGPointMake(69, 40);
tempVertices[2] = CGPointMake(48, 6);
tempVertices[3] = CGPointMake(17, 10);
//Then I pass the pointer to my array off to a setter...
[self setVertices: tempVertices];
However, when tempVertices gets created, it seems that I'm only getting space for one CGPoint:
int test1 = sizeof(CGPoint); // 8
int test2 = sizeof(tempVertices); // 4
int test3 = sizeof(*tempVertices); // 8
When stepping through with the XCode debugger, it shows that tempVertices is a pointer to a CGPoint. When I set tempVertices[0], the CGPoint that tempVertices points to recieves that value, which is reflected in the debugger. Where did my other 3 slots go? tempVertices seems to be pointing to a singe CGPoint instead of an array. I want the array.
Any ideas on what I'm doing wrong? I know that there are other ways to fix this using C++ or other objects, but I want to stick to C if possible.
Thanks in advance!
Update :
To answer zpasternack, setVertices: is a custom written setter. And I don't know how / if it knows how big the incoming array is. I'm trying to understand straight C stuff better, so insights/explanations regarding the proper way of passing a dynamic C-array as an argument are highly appreciated. Here's what the setter looks like :
- (void) setVertices:(CGPoint*) val {
_vertices = val; //_vertices is a member variable of the type CGPoint*
//...calculate a centroid, other stuff...
}
If needed, I could wrap my CGPoints in NSValue objects and use an NSArray instead, but I sure would like to know the right way of doing it in plain ol' C.
Thanks to everyone who has commented - you guys are great :)
On your 32 bit machine, you're getting exactly what you expect. sizeof(tempVertices) is the size of the pointer, while sizeof(*tempVerices) gives you the size of a CGPint (probably two ints). You can't get the size of an allocated array with sizeof(). The value is only known at run-rime, and sizeof() is a compile time operator.
OK, after your edit I think I see what's going on. That code, exactly as you've written, should work OK. Xcode won't show you the values of any of those CGPoints, because it doesn't know it's an array, just a pointer to a single CGPoint. But it's there. Set a breakpoint right after you call setVertices:. At the gdb prompt, print some of those values.
(gdb) print _vertices[1]
$2 = {
x = 69,
y = 40
}
(gdb) print _vertices[3]
$3 = {
x = 17,
y = 10
}
Correct, see?
That's not to say there aren't issues here. For one thing, setVertices: is leaking that memory. You're allocating memory for tempVertices, holding onto that pointer, but not freeing it anywhere. The next time you call setVertices:, you'll have a leak.
A bigger issue is that nobody knows how many CGPoints are in that array, except the code that allocated the memory for it. Will it always be 4 CGPoints? What happens if somebody accesses _vertices[5] or _vertices[27]? Bad things, if you didn't allocate that much space for them.
Is there a requirement that this be a plain C array? Like, these points are going to get passed to OpenGL or cocos2d or something? If not, you might consider using some kind of array class for it. Because these aren't NSObject-derived objects you're storing, you can't use an NSArray. You could use a std::vector, if you don't mind dragging in a buncha C++. I probably would not do that.
If you're set on sticking with a C array, you should probably do some work to try to make the interface less error prone. Like I mentioned before, you'll need to track the size of the array. Perhaps you could add a parameter to setVertices: representing the number of CGPoints that the array holds. Then other parts of the code that access _vertices could check that to make sure they're not walking off the end of the array. And, like I mentioned before, make sure you free that memory before you reassign the pointer.
Messing about with pointers is fraught with danger. Tread carefully, there be dragons there.
The malloc is allocating enough space for 4 GCPoint structs and returning a pointer to the allocated space.
The first is at tempVertices + 0. It's tempVertices[0].
The second is at tempVertices + 1. It's tempVertices[1].
The third is at tempVertices + 2. It's tempVertices[2].
The fourth is at tempVertices + 3. It's tempVertices[3].
I would not use sizeof() to determine the size of an array allocated at runtime.
Have you actually had trouble assigning new CGPoint objects into your array? Does CGPointMake() perform any allocation of its own?

Passing a pointer that is part of an object (Cocoa)

OK, I have a custom object (an NSManagedObject subclass, if it matters) and I want to pass a pointer to one of its iVars to a function that I've set up to modify such values. With a normal pointer you'd just prefix it with an ampersand (&) as in the classic NSError &error example, but that can't be done with dot notation. I can't just pass &object.iVar as I'd hoped. Can anyone suggest a simple and elegant way to obtain the pointer of iVar so that I can pass it? I am loath to pass the entire object for reasons of code structure and neatness.
-Ash
Argh, as is almost always the case, I ask a question after an hour of frustrating puzzling then ten minutes later answer it myself. I don't know, maybe asking questions is some kind of therapeutic trigger for answers... shame this isn't a psychology website.
Anyway, my solution was to add a new 'pseudo-getter' method to the object I'm trying to access the pointer from that looks a bit like this:
- (Pointer **)getIVarPointer
{
return &iVar;
}
It's a bit cludgy, but since I only have that one iVar whose pointer I need to obtain it's not too bad. On ther other hand if there is a simpler, more 'official' way of doing this, I'd love to know it!

Storing a C struct in CFMutableDictionary

Since there is no counterpart to NSValue in Core Foundation, how are we supposed to store C structs in a CFMutableDictionary?
First, you can put an NSvalue in a CFMutableDictionary as-is, so the answer is "use NSValue." But I assume the rest of your question is "without using any Cocoa objects." In that case, just create a non-retaining CFMutableDictionary, and you can put any pointer you want into it. See "Defining Custom Collection Callbacks" for some example code. I use these a lot.
Remember that these still have to be pointers, so you're going to have to allocate your structs on the heap, not the stack. And that means that memory management is now your problem. Of course you could create a custom set of callbacks that do whatever you want, so if you're using boost or something else with its own ref-counting system, you can still implement that with CFMutableDictionary.
And of course you can replace the struct with a small data object. That's usually a lot easier. But different problems need different solutions.
CFMutableDictionary
CFDictionaryAddValue
A CFType object or a pointer value to add to the dictionary.
you just pass a pointer to your struct.