Typecasting to UILabel in Objective-C - ios

I have an NSArray made of UILabels that I initialized with this statement (the actual pointer is created in the .h file):
interLabels = [NSArray arrayWithObjects:inter1,inter2,inter3, nil];
Later on I have an IBAction method that responds and is supposed to update the array of labels when a button is clicked:
-(IBAction)intervalButton:(id)sender{
int count = 0;
double val[3];
if(count < 3){
val[count] = number;
[interLabels objectAtIndex:count].text = [NSString stringWithFormat:#"%.2f", val[count]];
count++;
}
}
However, [interLabels objectAtIndex:count] doesn't seem to be recognized as a UILabel object, so I get a compiler error that states that the property "text" cannot be found on object type "id." How can I get the compiler to recognize this as a UILabel? Is this an issue that can be solved by typecasting?

objectAtIndex returns you an reference of type 'id'. You need to cast it to UILabel before the compiler / IDE will recognise the text property.
E.g.
((UILabel*) [interLabels objectAtIndex:count]).text = ...

Type id does not need to be casted if you assign it to another variable. And in your case i think it would be nicer since you actually do two things, first get a object from the array and then changes state on that object. Consider this.
-(IBAction)intervalButton:(id)sender
{
for ( UILabel *label in interLabels )
{
label.text = [NSString stringWithFormat:#"%.2f", number];
}
}
I assumed you wanted a loop not a if statement since the if statement always evaluated to YES. Also i assumed number is a instance variable in your class.
Actually when i looked at the code again you can probably remove most of it.

Related

How can I make NSObject that is created in runtime key value coding-compliant

First of all I want to point out that yes there are a lot of questions on this subject on stack overflow but none that was of any help. I also tried asking the owners of these for advice but was unable to get in touch with any of them.
Here is my scenario. I'm receiving data from an API which is an array of objects. These object are all the same structure but they change dynamically from API end point. When I made an NSArray of NSDictionary and tried to set my grid data source with the value of the provided array. It didn't work. When I looked at the documentation IGGridViewDataSourceHelper I found out the following piece of information "As of right now, the data must be of a derivation of NSObject and have at least one property". So I started thinking of a way to create an NSObject at run time. I was able to find some resource on Apple Developers documentation to make that.
Given that the variable dictionary is given in a function
Kindly check the following
- (NSArray *)getRecrodsFromDictionary: (NSDictionary*)dictionary {
// the following include the array that I want to turn into objects
NSArray * response = [self parseKey:#"responseDetails" fromDictionary:dictionary];
NSMutableArray * rows = [[NSMutableArray alloc] init];
if ([response count] != 0) {
// 1. get all NSDictionary keys
NSDictionary * temp = response[0];
NSArray * keys = [temp allKeys];
// 2. create a class
Class ModelClass = objc_allocateClassPair([NSObject class], "WidgetDetailsModel", 0);
// 3. all class variables with the same name as key retrieved from NSDictionary
for (NSString * key in keys) {
NSString * currkey = [key capitalizedString];
const char * name = [currkey UTF8String];
class_addIvar(ModelClass, name, sizeof(id), rint(log2(sizeof(id))), #encode(NSString));
}
// 4. register a class to be used
objc_registerClassPair(ModelClass);
for (NSDictionary * curr in response) {
// create object
id MC = [[ModelClass alloc] init];
for (NSString * key in keys) {
// set values
const char * name = [key cStringUsingEncoding:NSASCIIStringEncoding];
Ivar CurrVar = class_getInstanceVariable(ModelClass, name);
NSString * newValue = [curr objectForKey: key];
object_setIvar(MC, CurrVar, newValue);
}
// add object to array
[rows addObject:MC];
}
}
return [rows copy];
}
Once I get the return value and try to set it to data source data variable I get the following run time error.
[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key AssetsClass.
I can't find any thing on how to make the created in runtime NSObject key value coding-compliant. How can I make it key value coding-compliant?
Edit 1:
I managed to bypass the runtime error by making the fields names capitalized.
Now the table is being populated with empty data (same number of rows as the data but empty text in it) which was the correct thing to happen because the values of the iVar is not retained. How Can I retain it?
Edit 2:
I'm still not able to retain the iVar value so I changed the location of the function to the same UIView class which then it did retain it for the short period of time I had to set the grid data source data value.
I'm curious to know if there is a way to make the iVar retained or set one of its attribute to be strong/retain to mark it for the deallocation process.
After long search on Google, StackOverFlow and other iOS related forums and research. Here is the conclusion that I was able to find. Ivar in objective-c will always be weak reference. In other words there is no way (that I can find) that makes the Ivar strong reference. This can only be achieved throw property with setting the attribute of each property made.

change property object will not affect property

I would like to verify something that I always use but when I think about it ... I get confused why it worked that way and I sure I read the explanation about it but I cant find it.
As far as I understand apple create their setter as something like this.
-(void)setString:(NSString *)value {
if (_string != value) {
[_string release];
_string = [value retain];
}
}
Now usually I create properties like this.
#property (nonatomic) NSString *string;
#synthesize string = _string;
The question is about next code:
NSString *s = #"Should be deleted";
[self setString:s];
NSLog(#"string check111 =%#",self.string);
s = NULL;
NSLog(#"string check222=%#",self.string);
The same output will be generated. From the setter I can see that my property points on the object that I changed but the property value will be the same.That situation triggers another question (if it works like that why would I need copy attribute).
Can someone provide a short explanation about it? (or concrete link to read).
Tnx A Lot. (I think my question may be already asked in the forum )
This has no effect because you are changing the object to which s points to.
This diagram probably explains it better, originally you have something like this:
Changing the point of s will not affect _string.
The idea of setting the property to copy is in case you set your string property to a mutable string and then change the content of it. See this question.
I guess it would be something like this
NSString *s = #"Should be deleted"; // create autoreleased string
[self setString:s]; // retain string
NSLog(#"string check111 =%#",self.string);
s = NULL; // reset pointer value to null. This operation doesn't affect string object
NSLog(#"string check222=%#",self.string);
// string's retain counter will be decreased by autorelease pool later

NSMutableArray Allocate then replaceObjectAtIndex

I have a NSMutableArray that i define in the header file as:
#property (strong, nonatomic) NSMutableArray *tempPhotosArray;
Then i allocate as:
_tempPhotosArray = [[NSMutableArray alloc] init];
What i'd like to know is if i then go to replaceObjectAtIndex the program will complain on an out of bounds. I want to keep only a set number of items in that array, so is it possible to do a insert or replace? i.e. if at index 0 it is empty do an insert, if there is an object already replace it?
Thanks
i think i agree with Hani Ibrahim. Since you said you only want to keep a set number of objects in the array. So how many you want?
// add these code when you initialize the array
int aSetNumber = 5;
_tempPhotosArray = [[NSMutableArray alloc] init];
for (int i = 0; i < aSetNumber; i++)
{
[_tempPhotosArray addobject: [NSNull null]];
}
i guess then you can do whatever you want, i don't know what exactly you want to do in this case, but i would check if the object in that position is NSNUll, if so, replace that, if not, i don't know what you want them
//use these code when you trying to insert the real object
if([[_tempPhotoArray objectAtIndex:anIndex] isKindOfClass: [NSNull class]])
{
//replace it here
}
As to why you are getting an error, what everyone else wrote is accurate, but....
The description of what you want doesn't match what an NSArray is. It sounds like you want a list of up to 5 items and never more than 5. It might be that if you try to add a 6th item the "oldest" goes away. Like a "recently opened" file history. You can make this type of functionality with an NSArray, but that's not what it is out of the box.
I would suggest making your own object class. I'm not going to write all the code for you, because this sounds suspiciously like programming homework, but I will point you in the correct direction.
FivePack <-- our class
NSArray *storage; <-- where we house the data
// a public method which lets you add things.
- (void)addItem:(id)item {
int indexOfLastItemInArrayToSave = 4;
if (storage.length < 4)
indexOfLastItemInArrayToSave = length-1;
NSRange range = NSMakeRange(0, indexOfLastItemInArrayToSave);
NSArray *temp = [storage subArrayWithRange:range];
// now create a new array with the first item being "item" that
// was passed in and the rest of the array being the contents of temp.
// Then save that to storage.
}
What you want to do with the data and writing something to get it from your new object is up to you, because I'm not sure how you want to do it.
There are no objects in the array when you initially created it, so there is nothing to replace.
Like this?
if([_tempPhotosArray count] > 0)
//replace object
else
//add object to array

Error in property of NSString Object when using in different method of same class?

I made a property of NSString and initialised it in a function of the class.
Now I am using NSLog() another function , to show what this string contains.
Instead of getting what I entered, I am getting weird string.
My code listing was following:
#property(nonatomic, retain) NSString *stringWithProperty;
Now in implementation:
- (IBAction)FirstButtonPressed:(id)sender
{
NSString *x= [labelForSecondLine.text substringWithRange:NSMakeRange(0, 2)];
NSString *y= [labelForSecondLine.text substringWithRange:NSMakeRange(3, 2)];
NSString *z= [labelForSecondLine.text substringWithRange:NSMakeRange(5, 2)];
stringWithProperty=[NSString stringWithFormat:#"%#-%#-20%#",x,y,z];
}
- (IBAction)secondButtonPressed:(id)sender
{
NSLog(#"%#",stringWithProperty);
}
When I press second button after pressing first, I get this output:
<UIButtonContent: 0x71533b0 Title = (null), AttributedTitle = (null), Image = (null), Background = (null), TitleColor = UIDeviceWhiteColorSpace 1 1, ShadowColor = UIDeviceWhiteColorSpace 0 0.5>
Can anyone tell me whats going wrong with me or with this code?
Properties are weird, until you get used to them. (In fact, they're weird even after you get used to them.)
Complicating matters is the fact that Apple keeps playing with the way properties work.
If you want to be relatively independent of compiler version, and avoid assorted oddities, insert #synthesize stringWithProperty; just below your #implementation statement, and always refer to the property as self.stringWithProperty. In later versions of the compiler these steps are unnecessary, but there is no harm in them.
if there is no #synthesize stringWithProperty=stringWithProperty; for property, underlaying variable name will be set to _stringWithProperty. You're hitting some object with same name (looks like it is some button content). Use self.stringWithProperty to access property or (if you really need to do so) _stringWithProperty to access it's variable directly (or maybe even better more explicit self->_stringWithProperty)

Objective C NSString* property retain count oddity

I have the following example class:
Test.h:
#interface Test : UIButton {
NSString *value;
}
- (id)initWithValue:(NSString *)newValue;
#property(copy) NSString *value;
Test.m:
#implementation Test
#synthesize value;
- (id)initWithValue:(NSString *)newValue {
[super init];
NSLog(#"before nil value has retain count of %d", [value retainCount]);
value = nil;
NSLog(#"on nil value has retain count of %d", [value retainCount]);
value = newValue;
NSLog(#"after init value has retain count of %d", [value retainCount]);
return self;
}
Which produces the following output:
2008-12-31 09:31:41.755 Concentration[18604:20b] before nil value has retain count of 0
2008-12-31 09:31:41.756 Concentration[18604:20b] on nil value has retain count of 0
2008-12-31 09:31:41.757 Concentration[18604:20b] after init value has retain count of 2147483647
I am calling it like:
Test *test = [[Test alloc] initWithValue:#"some text"];
Shouldn't value have a retain count of 1? What am I missing?
Thanks for your help.
Don't look at retain counts. They're not useful and will only mislead you — you can't be certain that nothing else is retaining an object, that an object you get from somewhere isn't shared.
Instead, concentrate on object ownership and follow the Cocoa memory management rules to the letter. That way your memory management will be correct no matter what optimizations Cocoa may be doing behind the scenes for you. (For example, implementing -copy as just -retain for immutable objects.)
Furthermore, it's critical to understand the difference between properties of your objects and instance variables within your objects. In your question's code, you are assigning a value to an instance variable. That instance variable is just that: a variable. Assigning to it will behave like any other variable assignment. To use the property, you must use either dot syntax or bracket syntax to actually invoke the property's setter method:
self.value = newValue; // this is exactly equivalent to the next line
[self setValue:newValue]; // this is exactly equivalent to the previous line
The code generated for the dot syntax and the bracket syntax is identical, and neither will access the instance variable directly.
You are passing in a literal string. The compiler probably allocates it in static memory and sets the retain count to the maximum possible value.
Try a dynamically allocated string instead and see what happens.
NSString* string = [[NSString alloc] initWithString: #"some text"];
Test* test = [[Test alloc] initWithValue: string];
You've got a reference to an immutable string. Assignment doesn't need to copy the value (the string data) since it's immutable. If you do a mutable operation, like value = [newValue uppercaseString] then it should copy the bits into value, and value's retain count incremented.
You're passing in a string constant, which can't really be deallocated. I think that 2147483647 is probably UINT_MAX, which basically means that the object can't be released.
I think you want to do this:
self.value = newValue;
which will invoke the property setter and cause the copy to occur. "value = newValue" simply assigns a pointer value to the instance variable.
You shouldn't be paying attention to the retain counts, just follow the Cocoa memory management rules. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html
hmm.. we're getting closer.
it appears that newValue's retain count is also 2147483647.
I tried dynamically allocating the string instead with the same retain count results.
I found a helpful article here: http://www.cocoadev.com/index.pl?NSString
FTA:
Does the NSString returned by #"" need to be released, or is it autoreleased?
Neither. #""-strings are of class NSConstantString?, and thus act like atoms in lisp; they hang around. That is, if you use #"cow" in two separate places in your code, they will be referencing the very same object.
I don't think -release or -autorelease does anything to either of them.
If I have "copy" on the property though, shouldn't it copy the contents of the target memory into new memory with a retain count of 1? It would seem the copy attribute does nothing in this case?
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
char *cstr = "this is a c string";
NSString *str = [[NSString alloc] initWithUTF8String:cstr];
NSLog(#"rc1: %d", [str retainCount]);
[pool drain];
return 0;
}
If you run the above code, it will display a retain count of 1
In Cocoa, many immutable objects will simply retain themselves when you ask for a copy within the same zone. If the object is guaranteed not to change (i.e. its immutableness) then an exact duplicate is redundant.
In Objective-C, the constant string class is separate to Cocoa's NSString class, although it may be a subclass of NSString (I'm not too sure). This constant string class may override NSObject's methods like retain, release and dealloc so that they do nothing, and also override retainCount so that it always returns the same number, UINT_MAX or so. This is because an Objective-C constant string is created in static memory. It must have the overall general behaviour of a Cocoa object (when using Cocoa) so that it can be added to arrays, used as keys to a dictionary etc, except in regards to its memory management, since it was allocated differently.
Disclaimer: I don't actually know what I'm talking about.

Resources