I am new to iOS, if you look at the following code, I would expect both x and y to be "hello"
- (void)viewDidLoad {
[super viewDidLoad];
NSString *x,*y;
y= [x getstring];
}//viewDidLoad
-(NSString *)getstring{
return #"hello";
}
Yet, I get this error: No visible #interface for NSString declares the selector 'getString'
I tried many things, I defined getstring in the .h file
Your x variable is type on NSString ant there is no method getString defined there.
I believe you want to call [self getstring] instead:
y = [self getstring];
Related
I have read about __bridge, _bridge_retain and _bridge_transfer and did some experiments. However the output does not coincide with what I was expecting. In particular, I have the following code:
#interface ViewController ()
#property (nonatomic, strong) NSString *test;
#end
#implementation ViewController
CFStringRef cfString;
- (void)viewDidLoad
{
[super viewDidLoad];
self.test = #"123";
cfString = (__bridge CFStringRef)self.test;
self.test = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"%#", cfString);
NSLog(#"%#", self.test);
}
I expect the program to crash, based on the following reasoning: _bridge does not transfer ownership, so while casting self.test to cfString, there is no retainCount increment. Once self.test is set to nil, ARC will step in and dealloc the string. So that portion of memory is freed, but cfString is still pointing there, resulting in a pointer bad access exception. In contrast to my reasoning, the output is 123 and null, and of course the program does not crash.
Moreover, if I replace
self.test = nil;
with
CFRelease(cfString);
I expect the program to crash as well due to a similar reasoning. Even stranger is that the output is now 123 and 123.
Can anyone kindly elaborate why? Btw, the term ownership always troubles me, some explanation will be greatly appreciated.
Your problem is that you're using a constant string. This is put straight into the programs memory so the reference is unexpectedly remaining valid despite the fact that it shouldn't. Use something less constant than a constant string and your program will brake like you think.
The problem is that you base your example on a literal NSString value.
In objective-C, constant NSString (constant values known at compile time) are never released. In fact, their main memory managment methods are like:
+ (id)allocWithZone:(NSZone *)zone {
id _uniqueInstance = [self _singletonInstanceOfClass];
if( _uniqueInstance == nil )
_uniqueInstance = [super allocWithZone:zone];
return _uniqueInstance;
}
- (id)copyWithZone:(NSZone *)zone {
(void)zone;
return self;
}
- (id)retain {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax; // denotes an object that cannot be released
}
- (oneway void)release {
//do nothing
return;
}
- (id)autorelease {
return self;
}
As you can see, releasing them is not possible.
Here is a class for a deck of cards that could have several different configurations that I define using a NSDictionary with string keys and array values of how the cards are to be added. I haven't completed the init function yet, but it gives me the error above on trying to access my NSDictionary property. Fairly new to objective-c sorry if this is trivial question.
Here is my .m class file:
#interface MarioCardDeck()
#property (strong, nonatomic)NSDictionary *cardConfigurations;
#end
#implementation MarioCardDeck
- (instancetype)init {
self = [super init];
if(self) {
unsigned index = arc4random() % [[cardConfigurations allKeys] count]; ** error line
}
return self;
}
- (NSDictionary *)cardConfigurations
{
if(!_cardConfigurations)
{
_cardConfigurations = #{
#"1" :
#[#"flower",#"coin20",#"mushroom",#"star",#"oneUp",#"flower",#"oneUp",#"flower",#"coin10",#"mushroom",#"coin20",#"star",#"mushroom",#"coin10",#"star",#"mushroom",#"flower",#"star"],
#"2" :
#[#"flower",#"coin10",#"oneUp",#"flower",#"oneUp",#"mushroom",#"star",#"mushroom",#"coin20",#"star",#"mushroom",#"coin10",#"star",#"flower",#"coin20",#"mushroom",#"flower",#"star"]
};
}
return _cardConfigurations;
}
#end
You need:
unsigned index = arc4random() % [[self.cardConfigurations allKeys] count];
You need to access the property by using self.
FYI - you should use:
unsigned index = arc4random_uniform([[self.cardConfigurations allKeys] count]);
You need to refer to it by self.cardConfigurations.
You need to change
unsigned index = arc4random() % [[cardConfigurations allKeys] count];
to
unsigned index = arc4random() % [[self.cardConfigurations allKeys] count];
Accessing properties in the init method is, however, dangerous thing to do in Objective-C. If the getter is overridden in a subclass you might get a nasty surprise. I would make another property for the index, assign the instance variable to NSNotFound, and do the calculation the first the time the getter method is called. Also, you should use NSUInteger as the type for storing the index.
I'm new to IOS dev and am making simple programs this one is a hangman game.
I wanted to pick a random string from a plist file (completed).
I now want to compare the user input text (from a text field) and compare it to the string we have randomly picked from our plist.
Here is my code for MainViewController.m as it is a utility. Only the MainView is being used currently.
#import "MainViewController.h"
#import "WordListLoad.h"
#interface MainViewController ()
#end
#implementation MainViewController
#synthesize textField=_textField;
#synthesize button=_button;
#synthesize correct=_correct;
#synthesize UsedLetters=_UsedLetters;
#synthesize newgame=_newgame;
- (IBAction)newg:(id)sender
{
[self start];
}
- (void)start
{
NSMutableArray *swords = [[NSMutableArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"swords" ofType:#"plist"]];
NSLog(#"%#", swords);
NSInteger randomIndex = arc4random() % [swords count];
NSString *randomString = [swords objectAtIndex:randomIndex];
NSLog(#"%#", randomString);
}
This is where i would like to implement the checking
I have tried characterAtIndex and I can't seem to get it to work for hard coded placed in the string let along using a for statement to systematic check the string.
- (void)check: (NSString *) randomString;
{
//NSLogs to check if the values are being sent
NSLog(#"2 %#", self.textField.text);
}
- (IBAction)go:(id)sender
{
[self.textField resignFirstResponder];
NSLog(#"1 %#", self.textField.text);
[self check:(NSString *) self.textField];
_textField.text = nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self start];
}
To compare 2 strings: [string1 equalsToString:string2]. This will return true if string1 is equal to string2. To get the string contained in a UITextfield: textfield.text.
Given that it's a hangman game, I assume you are trying to see if a single letter is contained by a given string - so equalsToString: wouldn't be what you want.
Instead, probably better to use rangeOfString:options:
if ([randomString rangeOfString:self.textfield.text options:NSCaseInsensitiveSearch].location != NSNotFound){
// Do stuff for when the letter was found
}
else {
// Do stuff for when the letter wasn't found
}
Also, as was pointed out by Patrick Goley, you need to make sure you're using the textfield.text value to get the string from it. Same with storing the initial word you'll be using as the hidden word.
There are also a couple of other minor code issues (semicolon in the function header, for example) that you'll need to clean up to have a functioning app.
Edit: Made the range of string call actually use the textfield's text, and do so case-insensitive (to prevent false returns when a user puts a capital letter when the word is lower case, or vice-versa). Also included link to documentation of NSString's rangeOfString:options:
For your check method you are sending the UITextfield itself, instead of its text string. Instead try:
[self check: self.textfield.text];
You'll also need to create an NSString property to save your random string from the plist, so you can later access to compare to the textfield string like so:
declare in the interface of the class:
#property (nonatomic,strong) NSString* randomString;
in the start method:
self.randomString = [swords objectAtIndex:randomIndex];
in the check method:
return [self.randomString isEqualToString:randomString];
I am a beginner of objective c. I created unit test application. How am I call test method?
Bellow given my sample code.
my project ::
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
int a=25;
int b=25;
MyUnitTestTests *myValue;
if ([myValue respondsToSelector:#selector(testExample:testExample:)])
{
int c = [myValue testExample:25 testExample:10];
NSLog(#"value %i",c);
}
[super viewDidLoad];
}
Test method ::
- (int)testExample:(int )a testExample:(int )b
{
int c= a+b;
return c;
}
is there any error ??
I tried lot of time to do this simple test. but output was (value 0)
Your output is not correct
NSLog(#"value %d", c);
Thus, without %i and with %d it should be correct.
And there is another bug: the instance myValue was not initialized.
MyUnitTestTests *myValue = [[MyUnitTestTests alloc] init];
Instead of typing int c = [myValue testExample:25 testExample:10];
Just type [self testExample:25 testExample:10];
And print the log statement in your test method.
I recently rewrote some code in one of my classes, which gave me an error with an NSString. Here is what I have now:
My class header:
#interface MyViewController : UITableViewController {
NSString *myString;
}
#property (nonatomic, retain) NSString *myString; // Or copy instead of retain.
#end
And implemented some methods:
- (void)viewDidLoad {
myString = #"This is";
if (something) {
myString = [NSString stringWithFormat:#"%# a string.", myString]; // *1
}
[myString retain]; // <-- Why do I have to retain/copy here?
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
/* Some code for creating a UITextView called myTextView */
// ..and then setting the text property:
myTextView.text = myString; // <-- Crashes here if I don't retain/copy in viewDidLoad.
}
}
After some debugging I figured I had to retain/copy the NSString.
Why do I have to retain/copy the NSString in viewDidLoad if I want to use it later on?
Also, I noticed that if I remove the line marked *1, I don't have to retain/copy.
If you use the synthesized setter method to assign the variable, it will automatically retain the NSString for you:
if (something) {
self.myString = [NSString stringWithFormat:#"%# a string.", myString]; // *1
}
One way for you to better understand this would be to prefix your private attribute with an underscore _myString and keep your property as myString - this would make it easy to work out which is being used - the attribute or the property.
As you only seem to be using the value in your class I would question why you have a property at all as I think this is the source of the confusion.
If you have put the prefix in place then you will always know that _myString will need to have a retain when it is assigned to and myString will retain automatically.
The line at *1 is replacing the value of myString with a new string and for some reason this value is autoreleased before your code in the 2nd method is processed. I can't remember the exact reason, but I think this doesn't happen for the first assignment because you create a string literal with #"This is" which is not autoreleased.
I hope this helps.
You can use the retained property accessors which will release/retain automatically
- (void)viewDidLoad {
self.myString = #"This is";
if (something) {
self.myString = [NSString stringWithFormat:#"%# a string.", myString]; // *1
}
// no need to retain
}
The reason it does not crash when you remove the line marked *1 is because myString is still pointing at #"This is", which is a litteral, which lives in a corner of memory for the entire duration of the program and is never destroyed, hence that memory location remains valid.