In my header file I declared a new property like this:
#property (weak, nonatomic) NSString *porperty;
In my implementation file I give the property a value:
-method1{
self.property = someString;
NSLog(#"property = %#", self.property);
}
The log shows up in the debugger as the same value of someString. Okay great, but when i try to use this property in the next method of the same implemtation file it loses its value.
-method2{
NSLog(#"property = %#", self.property);
}
Now the debugger says (null).
Simple question. i know but this usually works for me. What am i doing wrong?
This is because someString has been released on next cycle.
Replace weak by strong, this will retain your string.
#property (strong, nonatomic) NSString *porperty;
Related
I'm in the process of introducing Swift to an Objective-C app.
I have a UIViewController subclass where I want to hide a button if a certain property is nil. My Swift code looks like this, using optional binding:
if let optionalVar = modelObject.propertyThatCouldBeNil {
button.setTitle(...)
} else {
button.hidden = true
}
My hope was that when propertyThatCouldBeNil was nil, the if wasn't satisfied and control would continue to the else. But that's not what's happening. In fact if I set a breakpoint, what I see is...
(lldb) po optionalVar
nil
(lldb) po modelObject.propertyThatCouldBeNil
▿ Optional<Optional<String>>
- some : nil
That latter one is what's tripping me up a bit. I think it should be Optional, not nested, correct?
Some more info...
Since I'm using modelObject throughout this class, for convenience it's defined as an implicitly unwrapped optional...
var modelObject : ModelObjectClass!
ModelObjectClass is actually an Objective-C class, and the property is read only and declared as such...
#property (readonly, nullable) NSString *propertyThatCouldBeNil;
In it's implementation, it actually acts as a sort of proxy for another ready only property...
- (NSString*) propertyThatCouldBeNil {
return self.someOtherProperty;
}
And the someOtherProperty is nullable as well...
#property (nullable, nonatomic, retain) NSString *someOtherProperty;
Any insight as to why the optional binding isn't working as I expect?
Seems to be working with me
#interface ModelObjectClass : NSObject
#property (readonly, nullable) NSString *propertyThatCouldBeNil;
#property (nullable, nonatomic, retain) NSString *someOtherProperty;
- (nullable NSString*) propertyThatCouldBeNil;
#end
#implementation ModelObjectClass
- (nullable NSString*) propertyThatCouldBeNil {
return self.someOtherProperty;
}
#end
Newbie to Objective-C....
I have a real simple .h file:
#interface IdentityManager : NSObject
#property (nonatomic, weak) NSString *username;
#property (nonatomic, weak) NSString *password;
#property (nonatomic, weak) NSString *connString;
#property (nonatomic, weak) NSString *token;
#end
And I want need to grab text from some text fields in another object to load into an Identity object:
self.identity.username = self.usernameTextField.text;
self.identity.password = self.passwordTextField.text;
Yep, it's a login page. Problem is that the username would not be set. After hours trying to find out why, I found that putting the value of self.usernameTextField.text into a local variable and passing the value of that to the Identity object worked:
NSString *tempUsername = self.usernameTextField.text;
self.identity.username = self.tempUsername;
self.identity.password = self.passwordTextField.text;
I have no idea why this would be. I can only guess that all my messing around has somehow left a trace of some old references in Xcode's cache somewhere.
More likely, I'm an idiot. Better, I'm still learning.
Should I be using NSMutableString?
I think something similar is happening again elsewhere. Use of a temp variable helping to achieve my goal.
Any thoughts anyone?
I don't really think your second solution actually fixes the issue. It's probably something else.
Though it's really wrong for a manager class to have a weak reference. In OOP, classes and especially managers should own the object they have as property.
So you should use strong references instead of weak:
#property (nonatomic, strong) NSString *username;
Additionally, you don't want any changes outside the class to modify the variable, so you should be passing a copy of the object:
NSString *username = self.usernameTextField.text;
self.identity.username = [username copy];
Alternatively, you can declare the property as copy instead of strong and you don't have to worry about copying the string every time you set it. (Credit to albertamg)
#property (nonatomic, copy) NSString *username;
I have an object called SCPFAd and it is declared in its header file as follows:
#interface SCPFAd : NSObject
#property (strong, nonatomic) NSArray *imageURLs;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *price;
#property (strong, nonatomic) NSString *longDescription;
#property (strong, nonatomic) SCPFLocation *location;
#property (strong, nonatomic) SCPFCategory *category;
#property (strong, nonatomic) NSArray *properties;
#property (readonly, strong, nonatomic) NSString *sellerID;
#property (readonly, strong, nonatomic) NSString *timePosted;
- (id)initWithRawData:(NSDictionary *)rawData;
- (BOOL)displaysPrice;
#end
In the implementation file, I have an SCPFAd extension declared this way:
#interface SCPFAd ()
{
NSMutableDictionary *_rawData;
NSMutableArray *_imageURLs;
NSString *_title;
NSString *_price;
NSString *_longDescription;
SCPFLocation *_location;
SCPFCategory *_category;
NSMutableArray *_properties;
}
#property (strong, nonatomic) NSDictionary *rawData;
#property (strong, nonatomic) NSString *sellerID;
#property (strong, nonatomic) NSString *timePosted;
#property (strong, nonatomic) NSString *adID;
#end
I deliberately redeclared the properties rawData, imageURLs, and properties as instance variables because I want external objects to access or assign them as immutable types, but I'll be changing them internally.
What I don't understand is why, when I override the setters, I get a compiler error that says it can't find the variables _title, _price, _longDescription, _location, and _category. The error goes away when I redeclare title, price, longDescription, location, and category as above, but I see it as unnecessary--nothing in the class extension changes their external declarations.
This is how I'm overriding setTitle, for example:
- (void)setTitle:(NSString *)title
{
_title = title;
_rawData[#"name"] = title;
}
- (NSString *)title
{
if (!_title) {
_title = _rawData[#"name"];
}
return _title;
}
If I comment out NSString *_title; in the extension, the compiler says it can't find _title in the first line of the setter, and wherever it occurs in the getter. The getter used to work just fine, though, even without the redeclaration.
If you declare a property and then override both the getter and setter, it won't auto-synthesize the property. But you can just add a line to synthesize it to your implementation:
#synthesize title = _title;
As for having a property be an immutable type, and its backing instance variable be mutable, you're going to have an issue when from outside your class the immutable type is assigned to it, and you treat it as the mutable version, because it won't respond to the methods to mutate it. For example, you assign an NSArray to a variable, then try to treat it as an NSMutableArray, it won't work.
If you implement a getter, the compiler doesn't automatically create an ivar.
This is for a good reason. The property may (and, in my experience, usually is) created on request and returned, so in that case no instance variable is needed to store it and it would add a significant memory overhead to classes with a large number of such properties if every getter had an associated ivar.
One other comment. This:
NSMutableDictionary *_rawData;
// ...
#property (strong, nonatomic) NSDictionary *rawData;
May cause you problems. If rawData is set with an immutable dictionary, it will raise an exception when you attempt to mutate it later. Make sure you copy it on assign using -mutableCopy. (I assume you aren't copying it because it's marked strong, not copy. If you are, it's fine)
When you override the setter and getter (not just the getter), Xcode assumes you want complete control and doesn't create the backing store (the _title). You have to do it yourself with
#synthesize title = _title
If you implement a getter and a setter for a read-write property, or a getter for a read-only property then Clang (Xcode) will not synthesise the backing instance variable - see Apple's Encapuslating Data, note in the section You Can Implement Custom Accessor Methods.
You are implementing both the setter and the getter so you must provide your own instance variable if needed.
I am trying to print (nslog) the name of a photo embedded in PhotoView object that I created. I have created 2 viewcontrollers class and the PhotoView class extending the UIButton class to populate a grid in one of the former viewcontrollers.
In PhotoView.h I have
#interface PhotoView : UIButton
#property (assign, nonatomic) NSString *photoName;
...
in PhotoView.m I have
self.tag = [[data objectForKey:#"PhotoID"] intValue];
self.photoName = [data objectForKey:#"PhotoName"];
After printing out the value of tag and photoName on the same file, everything looks good.
Problem start when I try to print the value of photoName from another class after clicking on the PhotoView
-(void)didSelectPhoto:(PhotoView*)sender
{
NSLog(#"%#", [NSString stringWithFormat:#"%#", sender.photoName]);
}
After clicking on the photoView, I get EXC_BAD_ACCESS error.
However, If I do
NSLog(#"%#",[NSNumber numberWithInt:sender.tag]])
I don't get this error.
What could possibly be wrong?
Thanks in advance.
Two remarks:
[NSString stringWithFormat:#"%#", sender.photoName] - No please! No! Don't do that! It's not only superfluous and wastes CPU cycles, but it also heavily decreases readability. if you have a string, you don't have to duplicate it like this, just use the string object directly:
NSLog(#"%#", sender.photoName);
The actual error you have is this:
#property (assign, nonatomic) NSString *photoName;
I. e. you have an assign property, so it doesn't retain its value. When your string object goes out of scope, it's deallocated (and since it's not weak, it isn't automatically set to nil, but it holds whatever garbage value that invalid pointer is, hence the crash). Write instead
#property (retain, nonatomic) NSString *photoName;
if you're not using ARC, and
#property (strong, nonatomic) NSString *photoName;
if you are.
This is my first time using this site and I am quite new to Objective-c. I'm sure this is a simple question but for some reason I am having a lot of issues. The app is designed to have the user enter a string via textfield, then it will pick the rest of the sentence and display it. The issue appears to be that my *name will be retained after the keyboard method and work once in the changelabel method. Then if i press the button again, invoking the changelabel method, the name appears to have been released and crashes the app.
#import
#import "Array.h"
#interface RandomBoredViewController : UIViewController {
UILabel *label;
UIButton *button;
UITextField *textField;
Array *array;
NSString *name;
NSString *description;
NSMutableString *whole;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) IBOutlet UIButton *button;
#property (nonatomic, retain) IBOutlet UITextField *textField;
#property (nonatomic, retain) Array *array;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSMutableString *whole;
-(IBAction) keyBoard;
-(IBAction) changeLabel;
#end
and my .m
#import "RandomBoredViewController.h"
#implementation RandomBoredViewController
#synthesize label;
#synthesize checker;
#synthesize button;
#synthesize textField;
#synthesize array;
#synthesize name;
#synthesize description;
#synthesize whole;
-(IBAction) changeLabel {
NSLog(#"Button being pushed");
description = [array getString];
NSLog(#"%#",description);
NSLog(#"%#",name);
name = [NSString stringWithString:name];
whole = [NSMutableString stringWithString:name];
NSLog(#"%#",name);
NSLog(#"%#",whole);
[whole appendString:description];
NSLog(#"%#",name);
NSLog(#"%#",whole);
label.text = whole;
NSLog(#"%#",name);
}
-(IBAction) keyBoard {
name = [NSString stringWithString:textField.text];
NSLog(#"%#",name);
label.text = [NSString stringWithString: name];
[textField resignFirstResponder];
}
- (void)viewDidLoad {
[super viewDidLoad];
array = [[Array alloc]init];
[array createArray];
NSLog(#"%i",[array arrayCount]);
whole = [[NSMutableString alloc]init];
name = [[NSString alloc]init];
}
- (void)dealloc {
[super dealloc];
[label release];
[button release];
[textField release];
[array release];
//[name release];
[description release];
}
#end
You are setting name to an autoreleased instance of NSString, this is probably what's causing your app to crash.
Use
self.name = [NSString stringWithString:textField.text];
Your synthesized mutator will retain the NSString and prevent it from being released.
Taking one thing in microcosm, the code you've posted creates two things named name — an instance variable and a property.
Instance variables are directly accessed storage. They have no behaviour.
Properties are named attributes accessed via getters and setters. So they may have arbitrary behaviour. They may report the values of instance variables or values calculated from instance variables or values calculated or obtained by any other means. Relevantly, the setters may retain, assign or act in any other way.
Instance variables may be accessed only by the instance of a class they belong to. Properties are usually intended to be accessed by anyone.
Since retaining is a behaviour and you've ascribed it to your name property, setting something to it would result in a retain. Instance variables can't have behaviour, so setting a value to it doesn't result in a retain or anything else.
As a result, this line:
name = [NSString stringWithString:name];
Creates a new string and returns a non-owning reference. Which means it'll definitely last for the duration of this autorelease pool (ie, you explicitly may pass it as an argument or return it safely, assuming you haven't taken manual control of your autorelease pools).
You store that reference to your instance variable. Instance variables don't have behaviour so the reference is stored but you still don't own that object. It's still only safe to use for the duration of that autorelease pool.
So when you access it in that method it's safe. When you access it later it's unsafe.
If instead you'd gone with:
self.name = [NSString stringWithString:name];
Then you'd have set that string to be the new value of the property. Because your property has the retain behaviour, you'd subsequently have completely safe access to the string object, until you say otherwise.
Because you've got a property with exactly the same name as an instance variable, you could subsequently access it either as just name or as self.name. Similarly you could have stored directly to the instance variable rather than via the property if you'd ensured you had an owning reference manually.
As suggested above, use of ARC is a way to get the compiler to figure all this stuff out for you.
That issue is what causes your code to crash — you end up trying to access a reference that has ceased to be valid. If you'd taken ownership of it then it would have continued to exist at least as long as you kept ownership.
try using self.name
sometimes this stuff confuses me as well and for that you might want to consider using arc in which case most of this stuff can be avoided.
when using properties you should always use self.propertyName vs propertyName (only), it uses the accessors (get propertyName, set propertyName) as opposed to directly accessing that pointers value.
take in mind there are 2 exceptions to the rule, init and dealloc which should NOT use self.
self.name = [NSString stringWithString:name];
you technically should also have an init method
to initialize your variables, and i believe you should call [super dealloc] last not first in your dealloc method, but thats not your problem and might not matter (just what I do when I dont use arc)
When you change your instance variable in changeLabel, you should release the previous value and retain the new one. You may use the accessors to perform the memory management stuff for you. Also, I think you should invoke [super dealloc] after releasing the instance variables in your implementation of dealloc.
If you're not familiar with Cocoa memory management (and even if you are), the best is to enable ARC (Automatic Reference Counting) and let the compiler deal with it.