Objective C fill vector. Push_back not working - objective-c

I am a newbie Objetive C developer and I am trying to fill up a vector which it is a variable property... But It doesn't working, I can not understand why or how to fix it
-(void) setUpLabels:(NSString *) _labels_path {
// Read the label list
self.label_strings.clear();
std::vector<std::string> new_values;
std::ifstream t;
t.open([_labels_path UTF8String]);
std::string line;
while (t) {
std::getline(t, line);
new_values.push_back(line);
}
self.label_strings.reserve(new_values.size());
for (int index = 0; index < new_values.size(); index++) {
self.label_strings.push_back(new_values[index]);
}
NSLog(#"size=%lu",self.label_strings.size());
t.close();
}
This is the .h file
#interface AIClassifier: Classifier
#property std::vector<std::string> label_strings;
- (id) init;
- (id) initWithParams:(NSString *)model:(NSString *) labels;
#end
The function setUpLabels is not in the file..
All the time is printing size = 0. I tried more simple versions with the same result.

One problem with this code is that you use #property and self.label_strings. #property creates a getter method that returns a value of type std::vector<std::string>. This is a value, not a pointer/reference, so it will be a new copy of the underlying object. It means that each time you call self.label_strings.push_back(x) or any other method, it operates on a copy of the initial object (which is always empty).
To solve this use _label_strings instead of self.label_strings, or use an instance variable for label_strings instead of a #property:
#interface AIClassifier: Classifier {
std::vector<std::string> label_strings;
}
#end
(Note: better move this to be a private instance variable in your .mm file)
If there's no particular reason to use C++ for this task, there are several Objective-C ways to read a file line by line.
If the file is small, you can read it completely into memory and then split into separate lines like here: https://stackoverflow.com/a/4466934/1009546
In Objective-C we use NSArray<NSString*>* instead of std::vector<string>.
Another likely reason you don't get anything is that either the _labels_path file path is wrong or that it doesn't exist on device.
You can set a breakpoint in this function and use Xcode step by step debugger to see what actually is going on and what you have in _labels_path and new_values at the time of execution.
Normally you shouldn't normally have to use C++ in Objective-C, especially not in interfaces. If you make your file ".m", and not ".mm", it will not allow C++ inside (only Objective-C).
Note 1: if you stick with C++, you don't need a second loop. Just assigning self.label_strings = new_values; will copy it (this is C++ magic).
Note 2: always check the file after opening it. In Objective-C you normally have NSError* returned from functions. In C++ you can check the failbit (see open docs example).
Note 3: Ideally use RAII to make sure that the file is closed. In this case t.close(); is not needed, because ifstream destructor will close it (see http://www.cplusplus.com/reference/fstream/ifstream/close/).

Related

Show warnings or errors when redeclaring a variable

It seems that the default behavior in XCode is to silently allow redefinition of local variables if they are declared in a deeper scope, but throw an error or warning otherwise. For example, XCode produces an error for "Redefinition of 'var'" if it is redefined in the exact same scope:
- (void) doStuff
{
NSString *var = #"Hello World";
NSString *var = #"Goodbye"; // Error on this line
}
Similarly, if I have an ivar called 'var', and I try to re-declare 'var' in a local method, XCode will produce a warning for "Local declaration of 'var' hides instance variable" when I try to use it:
//MyClass.h
...
#interface MyClass : NSObject
{
NSString *var;
}
...
//MyClass.m
...
- (void) doStuff
{
NSString *var = #"Hello World";
NSLog(#"%#",var); // Warning thrown on this line
}
So far this is what I would expect. However, if var is redefined in a deeper scope, such as an if block or for loop, XCode allows it, and the outer declaration is silently ignored:
NSString *var = #"Hello World";
if (TRUE)
{
int var = 0;
NSLog(#"%d",var); //prints '0', No errors or warnings
}
NSLog(#"%#",var); //prints 'Hello World'
Why is the last example silently allowed, but the other two are caught? Is there some option or flag I can toggle in XCode so that an error or warning would also be created in the last example? If XCode won't catch it for me, is there some code I could write to make sure variables are never redefined? Or is it just my responsibility to make sure I'm not re-using my variable names?
In the build settings (Xcode 5 & 6, at least) you can set a warning for Hidden Local Variables to YES.
The last example is behavior that Objective-C inherits from standard C. A variable's scope is determined by the bracing level. It's been that way since the earliest days for C. It's called variable shadowing, and it's actually pretty useful in ensuring that code keeps working even in the face of API changes in system libraries.
As far as why it's allowed, but the earlier examples aren't, that's a consequence of how Objective-C implements instance variables. The instance variables are essentially treated as local variables of each of the class's methods. So when you declare a local variable in a function that shadows an instance variable, it gets flagged as an error. Basically the first and second cases are treated as equivalent.
To get a warning for these cases, set the LLVM warning option Hidden Local variables to Yes.

Key for objc_setAssociatedObject keeps changing address

I'm trying to make a key for use in objc_setAssociatedObject, as in this question:
How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?
I have a MyConstants.h file which defines
static NSString *myConstant = #"MyConstant";
This file is then included in MyFramework via MyFramework.h
#import ...;
#import "MyConstants.h"
#import ...;
Then in my project, the framework is included in all files via the .pch header.
#import <Cocoa/Cocoa.h>
#import <MyFramework/MyFramework.h>
This all works as expected. The problem is that when I call objc_setAssociatedObject or objc_getAssociatedObject with myConstant as the key, it doesn't work. The reason is clear enough from the log:
- (void)doSomething:(id)key { //Gets called with myConstant
NSLog(#"Associating with key at %p", &key);
objc_setAssociatedObject(self, &key, object, policy);
}
- (void)doSomethingElse:(id)key { //Gets called with myConstant
NSLog(#"Seeking association for key at %p", &key);
NSLog(#"Will find %#", objc_getAssociatedObject(self, &key));
}
Which logs:
Associating with key at 0x7fff5fbfecdf
Seeking association for key at 0x7fff5fbfecef
Will find (null)
You'll notice that the pointers shift.
However, calling the same method again shows the same pointers. It's as if the constant IS a constant, in the file that imports it, but not for the whole project.
Q: How can I correctly define and import headers with constants so that a constant is actually at a constant place in memory?
If two files have static char something, each one will have its own copy--they do not represent the same piece of memory. If you want two files to have access to the key, you must instead do this in your header:
extern const char MyConstantKey;
And this in one implementation file (.c or .m):
const char MyConstantKey;
This defines a single one-char-wide location in memory, to which all files will point when you use &MyConstantKey.
I have edited the original post to show what I think the error was. The other poster's answer may well have also been an excellent solution, but how I resolved the problem was like so:
As the &key indicates, I was passing key directly to the methods, as in myMethod:(id)key. Somehow, de-referencing this pointer was different in different cases. By simply changing all the methods to myMethod:(void *)key and passing in &key, the problem immediately went away. Or ensuring I was using a static char and not a static NSString *
I don't understand it fully, but it works.

How do I write to an NSObject from within a C function that doesn't see Obj-C variables?

I'm trying to get some code going that lets me display raw trackpad data from my macbook pro, like the app FingerMgmt. Unfortunately, no one seems to have the source for FingerMgmt. I did find some other source code that kind of works, however. I was able to NSLog the data I wanted to see like this:
int callback(int device, Finger *data, int nFingers, double timestamp, int frame) {
for (int i=0; i<nFingers; i++) {
Finger *f = &data[i];
NSLog(#"Frame %7d: Angle %6.2f, ellipse %6.3f x%6.3f; "
"position (%6.3f,%6.3f) vel (%6.3f,%6.3f) "
"ID %d, state %d [%d %d?] size %6.3f, %6.3f?\n",
f->frame,
f->angle * 90 / atan2(1,0),
f->majorAxis,
f->minorAxis,
f->normalized.pos.x,
f->normalized.pos.y,
f->normalized.vel.x,
f->normalized.vel.y,
f->identifier, f->state, f->foo3, f->foo4,
f->size, f->unk2);
//todo-get data from raw C to obj-C variable
}
return 0;
}
But whenever I try to store any of the data to an Obj-c string or variable, the C code does not see the variable as having been declared. Because of this, I cannot write to any text fields or graphical displays in Obj-C, and I cannot store the data to a variable that Obj-c can access.
Basically, I need a way to write to an Obj-C variable or object from within the callback.
On a side note, I had a very similar problem with an iPhone app a while back, and I ended up fixing it by somehow declaring the app delegate within the C code and writing to or reading from the variable like this-
me.delegate=(id <UIApplicationDelegate,UITabBarControllerDelegate>)[[UIApplication sharedApplication] delegate];//allows access to the delegate within C function
me.delegate.number0=5;//writes to this variable in the delegate
For some reason, I can not seem to adapt this to my current situation. I always get the error that "me" is undeclared.
A Objective-C method can access instance variables because it is automagically passed a hidden parameter with the public name self - any reference to an instance variable, say fred, is translated by the compiler into a field reference, say self->fred (and a similar translation for property references).
For your C function callback to access the fields of any object (or call an object's methods) you need to pass the function a reference to the object. Two simple ways:
Add an argument to the function. Many C callback protocols include a general "user defined" values which is passed around as void *, if you are calling one of these pass your object reference as this value and cast it within the C function back to the correct Objective-C type.
Pass the object via a global (or file static) variable, e.g. static NSSomeType *objectForCallback;. This method works when you're stuck with an existing C callback protocol which doesn't support a user defined value. However it is not thread or re-entrant safe as you are sharing a single static variable.
In both cases make sure the objected is retain'ed if you're not using garbage collection.
In response to comment
Case 1: You will see C functions declared which (a) take a callback function and (b) a user-defined value to pass to that function on every call. For example:
typedef T ...;
T findMatching(T *buffer, // an array of T to search
size_t count, // number of items in array
int (*matcher)(T item, void *user), // user function for match, 1st arg is item, 2nd user-supplied value
void *userValue); // user-supplied value to pass to matcher
If you are faced with C function like this you can pass a (retain'ed if needed) Objective-C object as userValue and cast it back to its Objective-C type inside matcher. For example:
int myMatcher(T item, void *user)
{
NSMutableDictionary *myDictionary = (NSMutableDictionary *)user;
...
}
- (void) someMethod
{
NSMutableDictionary *sharedWithC = ...;
...
T found = findMatching(buffer, count, myMatcher, (void *)sharedWithC);
...
}
Case 2: Objective-C is (a superset of) C. You declare a global just as you would in C. For example (little checking, not thread safe):
static NSMutableDictionary *myGlobalDictionary = nil; // "static" makes the variable only visible to code in the same file
- (void) setupTheSharedDictionary
{
myGlobalDictionary = [[[NSMutableDictionary alloc] init] retain];
}
- (void) releaseTheSharedDictionary
{
if(myGlobalDictionary != nil)
{
[myGlobalDictionary release];
myGlobalDictionary = nil;
}
}
In response to second comment
I'm guessing you are trying to use some third party (Google?) code. That code defines a callback protocol - a C function type. You cannot just redefine that C function type adding an extra argument and expect the third party code to magically cope!
So unless you intend to change the C you can use the second approach - store the reference to Objective-C object in a global. In your case this will be something like:
static MT2AppDelegate *sharedWithCAppDelegateReference;
int callback(...)
{
...
[sharedWithCAppDelegateReference->L1 setStringValue:#"Hellofff"];
...
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
sharedWithCAppDelegateReference = self; // store so C can pick it up
...
MTRegisterContactFrameCallback(dev, callback);
...
}
But remember this is not thread or re-entrant safe - you are effectively passing a function parameter via a global variable. If you need it to be thread/re-entrant safe you need to get a bit more involved.

Large Hidden Constant in Objective C

I have a large constant (an NSString with 10^6 values). Because of its size I would like to declare it at the end of the source file (so I don't have to scroll through it every time I want to edit my code). Also because of its size I would like it to be a constant so I can load it at compile time instead of runtime. Also, because I do not want it accessible to outside users I do not want to declare it as extern in the header file.
I have it declared as a constant using the code below in the implementation file, however it is giving me a "Use of undeclared identifier 'hugeConstantString'" if I move it past the #end of the implementation (for obvious reasons).
NSString *const hugeConstantString = #"a_whooooooole_lotta_characters";
I've checked this out: Constants in Objective-C but it didn't tell me anything I didn't know already. Maybe my brain is fried, but: is there any way that I can define this huge constant AFTER my implementation and still have it accessible? Or if I declare it in another header file and import it, will it then be accessible to others?
Thanks!
I'm not sure such a large string is a good idea, but if you're going to use it, I suggest putting it in its own header file.
MyLongStringConstant.h
#define kLongString #"..."
MyClass.h
....
#import "MyLongStringConstant.h"
...
//Do something with kLongString
...
If you want to have it accessible in every file of your app, import the header inside your apps myApp_Prefix.pch file, which is imported into every file.
I am going to save the conversation of Why are you doing that and just post a simple solution for you. Thanks to Tommy in the comments here is a simpler version.
#import "LargeStringTest.h"
#implementation LargeStringTest
//Declare the string
static NSString *hugeConstantString;
- (id)init {
self = [super init];
if (self) {
NSLog(#"Large String %#", hugeConstantString);
}
return self;
}
//Place all other code here
//Assign the string
static NSString *hugeConstantString = #"a_whooooooole_lotta_characters";
#end

Creating global array & iterator

I am attempting to load up an entire array of NSManagedObjects into an NSArray, then use an integer iterator to iterate through the array when a button is tapped. xCode seems to dislike declaring the integer and NSArray in the .h, then used throughout different methods in the .m.
I was wondering what the appropriate path an experienced developer would take in solving such a problem.
The flow would be:
1. Load data into array.
2. Set label using information at index 0. int i = 0;
3. User taps button; i++, retrieve element at index 1.
and so on until the end of the array, or the user stops tapping the button.
Edited:
This is the code that works, but I feel is incorrect:
XYZViewController.h
#interface XYZViewController : UIViewController <NSFetchedResultsControllerDelegate>{
int index;
}
XYZViewController.m
import "XYZViewController.h"
- (void)function1{
index = 0;
}
- (void)function2{
index++;
}
-(void)function3{
NSManagedObject *obj = [results objectAtIndex:index];
}
Is this actually correct? It works, but not elegant; not at all.
Did you declare the integer and NSArray in your .h file outside of a class? if so, it would be defined in every compilation module that includes that file, which results in multiple symbols at linking time => error.
Solution: If you need the NSArray / int only in one .m file, move them there. Otherwise declare them as extern in the .h, and define them in exactly 1 .m file, like this:
// 1.h
extern int myInt;
// 1.m
#include "1.h"
int myInt;
// Use myInt
// 2.m
#include "1.h"
// Use myInt
The code you wrote is correct since you want to keep the visibility of the variable as private as possible. In this case it seems like you only need this variable in the XYZViewController.m file. In fact, you may want to consider prefixing it with #private to make it even less visible to other units.