How to input new instance of Object with multiple arguments into NSMutablearray - objective-c

I'm very new to Obj-C, been learning more Java and C++ lately.
I have two objects Friend and Foe which inherit the Character Object. Friend and Foe have slightly different attributes. I want all Friends and Foes to be in the same NSMutablearray. Can't figure out how to put these into the array. I get an error saying too many arguments, expected 1 have 4. For Foe its the same, but expected 1, have 5.
The Character Object
#import <foundation/foundation.h>
#interface Character : NSObject
#property NSString *name;
#property NSInteger strength;
#property NSInteger iff;
- (void) printDetails;
#end
#import <Foundation/Foundation.h>
#import "game_character.h"
#implementation Character
- (void) printDetails
{
NSLog (#"%# has strength %ld\n", self.name, self.strength);
}
#end
The Friend Object (The Foe object is similar with without intelligence and spell but has an alternate NSInteger attribute.
#interface Friend : Character
#property NSInteger intelligence;
#property NSString *spell;
- (void)printDetails;
#end
#import <Foundation/Foundation.h>
#import "game_character.h"
#import "friend.h"
#implementation Friend
-(void)printDetails
{
NSLog (#"%# has strength %ld\n", self.name, self.strength);
NSLog (#" ,Intelligence %ld, Spell %#\n", self.intelligence, self.spell);
}
#end
The Friend Input Method (I will have a similar method to input a Foe)
void input_friend()
{
#autoreleasepool
{
char str[30] = {0};
NSInteger strength;
NSInteger iff=1;
NSInteger intelligence;
NSLog(#"Enter character name\n");
scanf("%s", str);
NSString *name = [NSString stringWithUTF8String:str];
NSLog(#"Enter character strength\n");
scanf("%ld", &strength);
NSLog(#"Enter character intelligence");
scanf("%ld", &intelligence);
NSLog(#"Enter character spell\n");
scanf("%s", str);
NSString *spell = [NSString stringWithUTF8String:str];
My Error is here when I try to add the object to the array.
[characters addObject:name, strength, iff, intelligence, spell];
}
}
The Main so far. I intend to add a menu with option to add Friend or Foe to the array.
int main(int argc, const char * argv[])
{
#autoreleasepool
{
characters = [[NSMutableArray alloc] init];
void input_friend();
void input_foe();
}
return 0;
}

In this line, you are passing multiple arguments to add to your list of characters. However, this doesn't work since you want to add objects, which is why you got your error:
[characters addObject:name, strength, iff, intelligence, spell];
So you need to initialize a new Friend or Foe first, set its properties, and then add it to your array.
Friend *newFriend = [[Friend alloc] init];
newFriend.name = name;
newFriend.strength = strength;
// etc.
[characters addObject: newFriend];

Related

ObjectC-Why can't I get the properties correctly using the class_copyPropertyList function?

macOS 11.5.2
Xcode 13.2.1
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <iostream>
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class clazz = NSClassFromString(#"NSString");
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
const char* name = property_getName(properties[i]);
std::cout << name << std::endl;
}
free(properties);
}
return 0;
}
I will take some snippets of the output:
hash
superclass
description
debugDescription
hash
superclass
description
debugDescription
vertexID
sha224
NS_isSourceOver
hash
superclass
description
debugDescription
...
From the output, we can find that properties such as hash, description, superclass, etc. will appear repeatedly several times, while some properties (such as UTF8String) do not appear in the result list.
How should I get the list of properties correctly?
I would appreciate it.
The reason you're not seeing UTF8String come up as a property is that it's not declared as a property in the main declaration of NSString, but rather in a category. On macOS 12.2.1/Xcode 13.2.1, the declaration of NSString boils down to this:
#interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>
#property (readonly) NSUInteger length;
- (unichar)characterAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
#end
All other properties and methods on NSString are declared in categories immediately afterwards:
#interface NSString (NSStringExtensionMethods)
#pragma mark *** Substrings ***
/* To avoid breaking up character sequences such as Emoji, you can do:
[str substringFromIndex:[str rangeOfComposedCharacterSequenceAtIndex:index].location]
[str substringToIndex:NSMaxRange([str rangeOfComposedCharacterSequenceAtIndex:index])]
[str substringWithRange:[str rangeOfComposedCharacterSequencesForRange:range]
*/
- (NSString *)substringFromIndex:(NSUInteger)from;
- (NSString *)substringToIndex:(NSUInteger)to;
// ...
#property (nullable, readonly) const char *UTF8String NS_RETURNS_INNER_POINTER; // Convenience to return null-terminated UTF8 representation
// ...
#end
When a property is declared in a category on a type like this, it doesn't get emitted as an actual Obj-C property because categories can only add methods to classes, and not instance variables. When a category declares a property on a type, it must be backed by a method and not a traditional property.
You can see this with a custom class, too — on my machine,
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface MyClass: NSObject
#property (nullable, readonly) const char *direct_UTF8String NS_RETURNS_INNER_POINTER;
#end
#interface MyClass (Extensions)
#property (nullable, readonly) const char *category_UTF8String NS_RETURNS_INNER_POINTER;
#end
#implementation MyClass
- (const char *)direct_UTF8String {
return "Hello, world!";
}
- (const char *)category_UTF8String {
return "Hi there!";
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
Class clazz = NSClassFromString(#"MyClass");
printf("%s properties:\n", class_getName(clazz));
uint32_t count = 0;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
for (uint32_t i = 0; i < count; i++){
printf("%s\n", property_getName(properties[i]));
}
free(properties);
puts("-----------------------------------------------");
printf("%s methods:\n", class_getName(clazz));
Method *methods = class_copyMethodList(clazz, &count);
for (uint32_t i = 0; i < count; i++) {
SEL name = method_getName(methods[i]);
printf("%s\n", sel_getName(name));
}
free(methods);
}
return 0;
}
outputs
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
category_UTF8String
If you remove the actual implementations of the *UTF8String methods from the class, the property remains declared, but the category method disappears (because it doesn't actually have a synthesized implementation because of how categories work):
MyClass properties:
direct_UTF8String
-----------------------------------------------
MyClass methods:
direct_UTF8String
As for how to adjust to this: it depends on what purpose you're trying to fetch properties for, and why you might need UTF8String specifically.
NSString declares in its interface it implements methods, but it does not actually implement them, that is why when you print at runtime a list of the its methods it does not print what you expect.
The methods are implemented by other private classes, and when you initialize a new instance of NSString, instead of getting an instance of NSString you get an instance of that private class that have the actual implementation.
You can see that by printing the class type of a string, the following prints NSCFString or NSTaggedPointerString, not NSString:
NSString* aString = [NSString stringWithFormat: #"something"];
NSLog(#"%#", [aString class]);
And this prints __NSCFConstantString:
NSLog(#"%#", [#"a constant string" class]);
This pattern is called a class cluster pattern.
If you modify to dump the methods of the NSCFString you will get a "redactedDescription", it seems you are prevented to query these classes.

How to store the returning value of an NSArray.objectAtIndex to an NSString variable

I'm new to programming using objective c, I've only used java in the past. I'm trying to code a quick hangman game and I've come across errors that for the life of me I can't find a solution to.
So in this method, I'm trying to pick a random word from an NSArray and setting it equal to the instance variable word.
#import <Foundation/Foundation.h>
#interface Hangman : NSObject
{
NSString *word;
}
-(void) randomWord;
-(void) guessLettter: (char) g;
-(void) guessWord: (NSString*) guess;
-(void) displayLetters: (char) x;
#end
#import "Hangman.h"
#implementation Hangman
-(void) randomWord;
{
NSArray *array = [NSArray arrayWithObjects:#"Mercedes", #"Banana", #"Porsche",
#"Dinosaur", #"Blue", #"owls", #"chicken", #"Lollipop", #"Table",
#"Hello", #"Corn", #"Uniform",nil];
int num = 11;
NSUInteger r = arc4random_uniform(11);
word = *[array objectAtIndex:(NSUInteger)r];
}
But trying to set word equal to whatever object is returned is giving me an error about assigning NSString Strong to type 'id' and I don't know what 'id' is.
You have a simple typo. This line:
word = *[array objectAtIndex:(NSUInteger)r];
should be:
word = [array objectAtIndex:r];
or even better:
word = array[r];
Side note: Don't put the ivar in the .h file. Put it in the .m file. The public doesn't need to know about private details.
.h:
#interface Hangman : NSObject
-(void) randomWord;
-(void) guessLettter: (char) g;
-(void) guessWord: (NSString*) guess;
-(void) displayLetters: (char) x;
#end
.m:
#import "Hangman.h"
#implementation Hangman {
NSString *word;
}
-(void)randomWord
{
NSArray *array = #[#"Mercedes", #"Banana", #"Porsche",
#"Dinosaur", #"Blue", #"owls", #"chicken", #"Lollipop", #"Table",
#"Hello", #"Corn", #"Uniform"];
NSUInteger r = arc4random_uniform(11);
word = array[r];
}
Also get rid of the semicolon after the name of the randomWord method in the .m file.
And notice the use of modern array syntax.

Setting up a class identifier to be called when logging array data

Hey stackOverflow community. I am working through Big Nerd Ranch's Objective C book and have come across the fun chapter on defining and setting up classes (chapter 17 if you're familiar). In it the challenge has us write a program where we define a stock class with several properties and instance variables. I have been able to get the program to work as asked but I want to tinker a little with it to get it to also NSLog a stockName so I can see what stock is associated with its properties.
Basically, is there a way to make the code more concise for this block:
NSString *appleName = #"AppleInc";
[Apple setStockIdentifier:appleName];
Maybe more like this:
[Apple setStockIdentifier:"AppleInc"];
I tried setting the property as a char in the class file but couldnt get it to work. Im new to this but I'm thinking that declaring a new NSString for the stockIdentifier value is extra code that isn't needed. Any feedback would be greatly appreciated.
Below is what I have for the main file:
#import <Foundation/Foundation.h>
#import "StockHolding.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
StockHolding *Apple = [[StockHolding alloc] init];
NSString *appleName = #"AppleInc";
[Apple setStockIdentifier:appleName];
[Apple setPurchaseSharePrice:2.30];
[Apple setCurrentSharePrice:4.50];
[Apple setNumberOfShares:40];
StockHolding *HomeDepot = [[StockHolding alloc] init];
NSString *homeDepotName = #"Home Depot Inc";
[HomeDepot setStockIdentifier:homeDepotName];
[HomeDepot setPurchaseSharePrice:12.19];
[HomeDepot setCurrentSharePrice:10.56];
[HomeDepot setNumberOfShares:90];
StockHolding *Cisco = [[StockHolding alloc] init];
NSString *ciscoName = #"Cisco Inc";
[Cisco setStockIdentifier:ciscoName];
[Cisco setPurchaseSharePrice:45.10];
[Cisco setCurrentSharePrice:49.51];
[Cisco setNumberOfShares:210];
NSMutableArray *listOfStocks = [NSMutableArray arrayWithObjects:Apple, HomeDepot, Cisco, nil];
for (StockHolding *currentStock in listOfStocks) {
NSLog(#"%#, Purchase Share Price: %.2f; Current value: %.2f; Number of shares: %i",[currentStock stockIdentifier],[currentStock purchaseSharePrice], [currentStock currentSharePrice], [currentStock numberOfShares]);
}
}
return 0;
}
Below is the contents of StockHolding.h:
#import <Foundation/Foundation.h>
#interface StockHolding : NSObject
{
//char stockIdentifier;
float purchaseSharePrice;
float currentSharePrice;
int numberOfShares;
}
#property NSString *stockIdentifier;
#property float purchaseSharePrice;
#property float currentSharePrice;
#property int numberOfShares;
-(float) costInDollars; //purchaseSharePrice * numberOfShares;
-(float) valueInDollars; //currentSharePrice * numberOfShares;
#end
And here is StockHolding.m:
#import "StockHolding.h"
#implementation StockHolding
#synthesize currentSharePrice, purchaseSharePrice, numberOfShares, stockIdentifier;
-(float)costInDollars;
{
return (purchaseSharePrice * numberOfShares);
}
-(float)valueInDollars;
{
return (currentSharePrice * numberOfShares);
}
#end
This is simply a syntax error.
[Apple setStockIdentifier:"AppleInc"];
Should be...
[Apple setStockIdentifier:#"AppleInc"];

What's wrong with my Objective-C class?

I am having trouble with my Objective-C code. I am trying to print out all of the details of my object created from my "Person" class, but the first and last names are not coming through in the NSLog method. They are replaced by spaces.
Person.h: http://pastebin.com/mzWurkUL
Person.m: http://pastebin.com/JNSi39aw
This is my main source file:
#import <Foundation/Foundation.h>
#import "Person.h"
int main (int argc, const char * argv[])
{
Person *bobby = [[Person alloc] init];
[bobby setFirstName:#"Bobby"];
[bobby setLastName:#"Flay"];
[bobby setAge:34];
[bobby setWeight:169];
NSLog(#"%s %s is %d years old and weighs %d pounds.",
[bobby first_name],
[bobby last_name],
[bobby age],
[bobby weight]);
return 0;
}
%s is for C style strings (a sequence of chars terminated by a null).
Use %# for NSString objects. In general, %# will invoke the description instance method of any Objective C object. In the case of NSString, this is the string itself.
See String Format Specifiers.
On an unrelated note, you should look into Declared Properties and #synthesize for your class implementation. It will save you a lot of typing as it produces all the getters and setters for you:
person.h:
#import <Cocoa/Cocoa.h>
#interface Person : NSObject
#property (nonatomic, copy) NSString *first_name, *last_name;
#property (nonatomic, strong) NSNumber *age, *weight;
#end
person.m
#import "Person.h"
#implementation Person
#synthesize first_name = _first_name, last_name = _last_name;
#synthesize age = _age, weight = _weight;
#end
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
int main (int argc, const char * argv[])
{
Person *bobby = [[Person alloc] init];
bobby.first_name = #"Bobby";
bobby.last_name = #"Flay";
bobby.age = [NSNumber numberWithInt:34]; // older Objective C compilers.
// New-ish llvm feature, see http://clang.llvm.org/docs/ObjectiveCLiterals.html
// bobby.age = #34;
bobby.weight = [NSNumber numberWithInt:164];
NSLog(#"%# %# is %# years old and weighs %# pounds.",
bobby.first_name, bobby.last_name,
bobby.age, bobby.weight);
return 0;
}

No print output in Xcode

I have a program which runs fine, but I get no print output even though I have a NSLog file in main.m Can you tell me what's wrong? Thank you.
main.m
#import <Foundation/Foundation.h>
#import "Stockholding.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
StockHolding *stockA;
StockHolding *stockB;
StockHolding *stockC;
[stockA setPurchaseSharePrice:2.40];
[stockA setCurrentSharePrice:3.12];
[stockA setNumberOfShares:40];
[stockB setPurchaseSharePrice:1.50];
[stockB setCurrentSharePrice:1.41];
[stockC setNumberOfShares:35];
[stockC setPurchaseSharePrice:1.10];
[stockC setCurrentSharePrice:1.20];
[stockC setNumberOfShares:60];
NSArray *holdings = [NSArray arrayWithObjects:stockA, stockB, stockC, nil];
for (StockHolding *n in holdings) {
// Call the methods
float cost = [n costInDollars];
float value = [n valueInDollars];
NSLog(#"Bought stock for $%.2f, It is now at $%.2f, I have %d shares, They cost me $%.2f, Now they are worth $%.2f", [n purchaseSharePrice], [n currentSharePrice], [n numberOfShares], cost, value);
}
}
return 0;
}
StockHolding.h
#import <Foundation/Foundation.h>
#interface StockHolding : NSObject {
float purchaseSharePrice;
float currentSharePrice;
int numberOfShares;
}
#property float purchaseSharePrice;
#property float currentSharePrice;
#property int numberOfShares;
-(float)costInDollars;
-(float)valueInDollars;
#end
StockHolding.m
#import "StockHolding.h"
#implementation StockHolding
#synthesize purchaseSharePrice, currentSharePrice, numberOfShares;
-(float)costInDollars
{
return (purchaseSharePrice * numberOfShares);
}
-(float)valueInDollars
{
return (currentSharePrice * numberOfShares);
}
#end
You haven't actually created any of those StockHolding objects. Thus, your array is empty, and the loop doesn't do anything.
StockHolding *stockA;
is just a declaration of a pointer. You need to create the object to which it points; the usual procedure is this:
StockHolding *stockA = [[StockHolding alloc] init];
Since, under ARC, object pointers are initialized to nil (which means "no object"), you are passing nil as all of the arguments to arrayWithObjects:. nil being the sentinel value meaning "there are no more arguments", the array is created without contents.
With an empty array, for (StockHolding *n in holdings) doesn't have anything to enumerate over, so none of the code in the body of the loop, including your NSLog(), gets executed.