I can`t figure out why my NSString lost its value in a IBAction.
I have in my .m the variable declaration :
#implementation myfile.m
NSString *text2=nil;
In (void)viewDidLoad I assign the value to text2 correctly, but when I fired the IBAction the debugger tells me the and text2 has lost the value.
The IBAction routine is placed in the .m file.
Please, anyone can tell me what I doing wrong ?
Advanced thanks
I know this is old, but just if anyone else stumbles upon it.
if you use ARC the nil value you assign basicly tells the compiler to dealloc when possible.
just do
#implementation myfile.m {
NSString *stringName;
}
- (void)viewDidLoad {
stringName = #"some-value";
}
Related
I'm working on a demo app with iBeacons. At this moment I have the ranging for the beacons working in my firstviewcontroller, when I want to show a detailed view of the beacon data in a second view controller I do hear the the audible feedback on beacon change which means the loop of detecting closest beacons is still running in view controller 1 ... but how can I get updates from view controller 1 in my second view controller?
I tried passing them in the segue, but then it is static NSString data, can anyone help me on how to get the "live" data in the second view controller?
If you want to process ranged iBeacon data in more than one ViewController, you can set up ranging in your AppDelegate, then call public methods on each ViewController from there. The main didRangeBeacons: inRegion: in your AppDelegate can call the custom methods on each ViewController if that ViewController has been activated.
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
if (self.firstViewController != Nil) {
[self.firstViewController handleBeacons: beacons];
}
if (self.secondViewController != Nil) {
[self.secondViewController handleBeacons: beacons];
}
}
In order to do this as above, you must maintain properties for each ViewController on your AppDelegate:
#interface MyAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) FirstViewController *firstViewController;
#property (strong, nonatomic) SecondViewController *secondViewController;
#end
You can populate them from each ViewController like this:
- (void)viewDidLoad {
[super viewDidLoad];
MyAppDelegate *appDelegate = (MyAppDelegate) [[UIApplication sharedApplication] delegate];
appDelegate.firstViewController = self;
}
I have just upgraded to XCode5 and iOS7 and now my application has stopped working.
I am creating a new view based on a property of a current view, and I need to set some properties of the new view before I display it.
Previously, I did it like this :-
hqView *v = [[hqView alloc] initWithNibName:NULL bundle:NULL];
[v setProperty1:true];
[v setProperty2:false];
[self presentViewController:v animated:TRUE completion:NULL];
This then triggered the [viewDidLoad] method on the view controller, which had the following code in it :-
if ([self property1])
{
[list1 load]
}
else
{
[list2 load]
}
However now the [viewDidLoad] method is triggering as soon as I create the view, meaning that I am not able to set the properties before [viewDidLoad] is called and so it ALWAYS loads list2 regardless of what I actually want.
The thing is - this did NOT happen under iOS6, so I am wondering whether it is a new setting in XCode5 that has caused this to change, or if I am going to have to rewrite it to do what I need it to do?
You cannot know when viewDidLoad, viewWillAppear, etc... will be called.
My advice : Make a dedicated init method to your controller, something like :
#implementation hqView
- (instancetype)initWithProperty1:(BOOL)prop1 property2:(BOOL)prop2
{
// uses default NIB
self = [super initWithNibName:nil bundle:nil];
if (self){
[self setProperty1:prop1];
[self setProperty2:prop2];
}
return self;
}
#end
Set a breakpoint on your viewDidLoad method that is being called before your init method and you will be able to see what is causing the viewDidLoad to be called. you will probably find that it is being called because the view was referenced by some other code. this most often happens in a super class (like if you have a UIViewController superclass that implements common functionality for your view controllers). for example, if you accidentally put new code in that accessed self.view in a method in your superclass such as - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
you would notice this behavior. so make sure you are not accessing the view in any code before you wanted to. -rrh
I use KVC in my projects.
And, in one of my classes, I wrote the property :
#property ( nonatomic, strong ) NSString *notes;
I want to put a NSString object in that property :
And before setting the value, I want to test the class name of the destination.
a = [ newContainer valueForKey:#"notes"];
if( a != nil && ![ b isKindOfClass:[ a class ] ] )
// here b is the new NSString value
The result is that xcode indicates that the classes aren't the same !
(gdb) po [ b class ]
__NSCFString
(gdb) po [ a class ]
__NSCFConstantString
I read that is not very important and that __NSCFConstantString is a private subclass of NSString.
But, in my case, I need to check all properties of my object before updating it.
And I don't want to had in my code :
// OK, classes aren't the same ...
// ---- BUT WE MUST test it again to know if a is a NSString and b a subclass of NSString or anything else ...
beurk !
Is anyone have the same problem ?
Thanks a lot for your help !
Three points:
1- You should check against [NSString class], the publicly exposed class of your property, not against the class of the current value of your property.
Imagine what happens when you check against the value class, instead of the property class: after setting your property to a NSMutableString, which is a perfect instance of NSString, you could not any longer set it to a regular NSString (since NSString is not a subclass of NSMutableString). Your current problem is a variant of the one described in this paragraph, which may be easier to understand.
2- The test should be done in the class that owns the property, not outside as you are doing now. Because only that class is entitled to know about the type of object it accepts.
3- So. Use the standard validateValue:forKey:error: method, which is your friend here. This method would be implemented by the class, and it would make sure notes is a NSString. Outside of the class, you would not check the type of the value directly, but ask the container class to validate it.
And voilà !
I have an NSMutableArray called message that I've alloc'd in init and release in dealloc. I can usually use a string in a CCCallFuncND action without a problem, even if it's an index of an array, such as:
displayMessagePiece = [CCCallFuncND actionWithTarget:self selector:#selector(displayMessageBoxString : data:) data:[[NSString stringWithFormat:[labelPieces objectAtIndex:i]] retain]];
However, if I use my mutable string, I get a crash with a green arrow pointing to my line of code that I have it in, and it says "EXC_BAD_ACCESS" with some hexadecimal.
Here's my action and sequence where I'm trying to use NSMutableString:
id displayMessage = [CCCallFuncND actionWithTarget:self selector:#selector(displayMessageBoxString : data:) data:[[NSString stringWithFormat:[message copy]] retain]];
[self runAction:[CCSequence actions:displayMessage,nil]];
Notice I use [message copy], although I've tried also just message.
I smell bad practice:
NSString* s = [[NSString stringWithFormat:[labelPieces objectAtIndex:i]] retain];
[CCCallFuncND actionWithTarget:self
selector:#selector(displayMessageBoxString:data:)
data:s];
First of all you are retaining a string that you may or may not be releasing. There is no guarantee that the displayMessageBoxString selector ever gets called. For example, the node running the call func action may be removed from the scene before the selector is called, or you may be changing the entire scene before the selector is called. In both cases you will be leaking the string.
There's also no reason to use CCCallFuncND when there's CCCallFuncO. CCCallFuncND is particularly harmful when your project is using ARC. In that case you'd have to bridge cast the object to void*, at which point ARC might simply release the memory, rightfully assuming that non-ARC code now manages the object's memory. The same may actually be true for autorelease, but I'm not 100% certain about that.
So first order of business is to use CCCallFuncO and re-test if your problem is gone. Also rely on autorelease, do not retain the string since you will not be able to release it in all cases.
NSString* s = [NSString stringWithFormat:[labelPieces objectAtIndex:i]];
[CCCallFuncO actionWithTarget:self
selector:#selector(displayMessageBoxString:)
object:s];
Now if your minimum target is iOS 4.0, you should really be using blocks instead. This is particularly true if you need both sender and object at the same time. The block has access to the local scope, so you can use the simplest CCCallBlock action in this case:
NSString* s = [NSString stringWithFormat:[labelPieces objectAtIndex:i]];
[CCCallBlock actionWithBlock:^void(){
CCLOG(#"sender is: %#, string is: %#", self, s);
}];
Et voila, all problems solved and guaranteed not to cause any memory leaks.
Note that you can also write the block without void() like this:
[CCCallBlock actionWithBlock:^{
CCLOG(#"sender is: %#, string is: %#", self, s);
}];
Personally I find it helpful to remind myself and others about the return type and parameters, even if there are none.
Using WSDL2ObjC I am getting lot of classes which are subclasses of NSString.
I am finding it troublesome to initialize the NSString value of any object of those classes.
Say my class looks like this :
#interface mClass ; NSString {
int value;
}
Now in my code I would like to use objects of mClass as both NSString and also want to use its attribute value which is an integer.
How can I do that?
I am trying to use code like this
mClass *obj = [[mClass alloc] initWithString:#"Hello"];
But it's showing me an error saying I am using an abstract object of a class , I should use concrete instance instead.
If you really need make NSString subclass you should override 3 methods:
- (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer;
- (NSUInteger)length;
- (unichar)characterAtIndex:(NSUInteger)index;
For example:
MyString.h
#interface MyString : NSString
#property (nonatomic, strong) id myProperty;
#end
MyString.m
#interface MyString ()
#property (nonatomic, strong) NSString *stringHolder;
#end
#implemenation MyString
- (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer {
self = [super init];
if (self) {
self.stringHolder = [[NSString alloc] initWithCharactersNoCopy:characters length:length freeWhenDone:freeBuffer];
}
return self;
}
- (NSUInteger)length {
return self.stringHolder.length;
}
- (unichar)characterAtIndex:(NSUInteger)index {
return [self.stringHolder characterAtIndex:index];
}
#end
It might be smarter to use a wrapper class that internally uses NSStrings to do whatever operations or manipulations you are trying to do. However this will cause you to need to overload any functionality of NSString you want (such as getting the length of the string).
Or, you could create a category of NSString (found right next to Objective-C class in the new file window). This allows you to add any properties or methods that you wish to be "added" to the NSString class. Now just import this category wherever you wish to use it and you will have all of your custom functions available on any NSStrings objects.
Do you really need to subclass NSString? It’s a class cluster, which (apart from other things) means it’s hard to subclass. There’s a good post by Mike Ash on subclassing class clusters. If you didn’t know that class clusters existed you are probably new to Cocoa and in that case the best short answer is don’t try to subclass class clusters.
There’s also previous questions about subclassing NSString here on Stack Overflow. Next time you might want to search a bit before asking a new question.