replace a object in NSMutableArray - ios

I try to replace an object from my Array with a string. I use
#property (strong, nonatomic) NSMutableArray *myArray;
NSString *oldString = [NSString stringWithFormat:#"oldObject"];
NSString *toReplace = [NSString stringWithFormat:#"newObject"];
NSUInteger index = [self.myArray indexOfObject:oldString];
[self.firstLanguageArray replaceObjectAtIndex:index withObject:toReplace];
But every time I try to replace, the app will crash.
Edit: I logged the "index". I will become a Integer like 2147483647.

Probably because your call to indexOfObject returns NSNotFound.
NSNotFound is a constant that tells you that the oldString object is not found in your self.myArray dictionary, and as this constant has a value of (NSUInteger)-1 (which is equivalent to the unsigned value 2147483647 because of integer underflow), value that will obviously crash your app with an "Out of Bounds" exception.
The solution is to test whether the index != NSNotFound before using it. Or to make sure that your array self.myArray actually contains an object of type NSString whose value is "oldObject".
If, given your actual code, you expected oldObject to be present in your self.myArray, then think again, maybe log the content of self.myArray to lookup what it actually contains, and also check that this content is a string.
(if you see "oldObject" when logging the content of your myArray, that's probably just the description string of the object in your array, so the array does not contain the string "oldObject" itself, but an object whose description is "oldObject")
Side note: there is no need to use stringWithFormat if you don't use any format placeholder (like %d or %# etc). You should simply directly use the string constant in that case, no need to dynamically build a string if that string is a constant: simply use NSString* oldString = #"oldObject" and NSString* toReplace = #"newObject".

Related

If this is the right way to use a customised string property in Objective C, why can’t I extract the correct numeric value?

I am revising an early project where I use tags to identify 1-of-5, 1-of-16 or 1-of-10 UIButtons. I want to replace the tags with a customised property based on my understanding of this answer.
The property called myInfo consists of a string followed by an integer. This may well be a tag by another name but it makes a message source uniquely identifiable in a way that a simple integer tag does not, clearing magic numbers from my code and, hopefully, improving the documentation.
The property is created using a category
UIView+CustomProperties.m
#import "UIView+CustomProperties.h"
#implementation UIView (MyInfo)
-(void)setMyInfo:(id)info
{
objc_setAssociatedObject(self, "_myInfo", info, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(id)myInfo
{
return objc_getAssociatedObject(self, "_myInfo") ;
}
#end
And myInfo works when I import objc/runtime.h
UIView+CustomProperties.
#import <objc/runtime.h>
#interface UIView (MyInfo)
#property ( nonatomic, strong ) id myInfo;
#end
I call the category from the method (below) in the UIView where I create several sets of buttons.
// define type and number of 5, 16 or 10 buttons
switch (buttonCount) {
case 5:
roundButton.myInfo = [NSString stringWithFormat:#"transpose index %i", i ];
break;
case 16:
roundButton.myInfo = [NSString stringWithFormat:#"player index %i", i ];
break;
case 10:
roundButton.myInfo = [NSString stringWithFormat:#"note index %i", i ];
break;
default:
roundButton.myInfo = #“orphan button“;
break;
}
To identify a message source I have tried to strip away all non-numeric characters from myInfo using this method. However a problem appears in my selector method forButtons as I attempt to remove non-numeric characters
- (void)fromButtons:(UIButton*)button {
NSLog(#"Button %ld tapped", (long int)[button tag]);
NSLog(#"first, %#", button.myInfo);
NSString *newString = [[button.myInfo componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:#""];
NSLog(#"then, %#", newString);
NSLog(#"and %li", (long int)newString);
When I build and run and press button 1, the NSLog statements above produce the following log
2017-05-25 18:27:33.147 SatGam3[930:607301] Button 1 tapped
2017-05-25 18:27:33.147 SatGam3[930:607301] first, transpose index 1
2017-05-25 18:27:33.148 SatGam3[930:607301] then, 1
2017-05-25 18:27:33.148 SatGam3[930:607301] and 2070247168
Note that the long int value for the original tag is correct i.e. 1 whereas the long int recovered from the customised property is 2070247168.
Q.1 Firstly, is this approach correct ?
Q.2 If so, can someone please explain why am I extracting a 9-digit numeric value from myInfo ?
First, a problem that has nothing to do with your problem: Your use of "_myInfo" as the key here is slightly dangerous. In practice you're going to get away with it, but it relies on a compiler optimization that isn't promised. You're betting that the compiler (and possibly the linker) will ensure that all copies of the same constant string refer to the same memory location in the binary. That happens to be true, but it's not part of the language. Use a selector instead.
But that's not your problem. Your problem is this:
NSLog(#"and %li", (long int)newString);
newString is (unsurprisingly) an NSString*. So this points the address of that string. If you want to convert this into a number, you'll want to call -intValue on it.
That said, I wouldn't encode this data as a string. Encode it as a data object:
#interface ButtonInfo
#property (copy) NSString *name;
#property (assign) NSInteger index;
#end
(or name could be an enum if there are fixed set of them)
If you want to make this easier to read, add a -description method. Use the type system; it's there to help you. Don't try to encode a complicated type into a string.
Use below line of code :
NSLog(#"and %li", [newString intValue]);
Your approach is correct.

type casting in objective-c, (NSInteger) VS. integerValue

I don't very understand about the diference between (NSInteger)aNumberValue and [aNumberValue integerValue], then do some test. For example, here is a response data from server:
You can see it's an int but the value is hold by NSNumber. I retrieve the data by writting NSInteger count = (NSInteger) dic[#"count"];, and in Xcode debug area, saw this:
it's really a strange value but when I run po count and saw this:
anyway, the value is correct, but another strange thing is:
the number 2 is not less than 100!
Then I try NSInteger a = [dic[#"count"] integerValue] and saw the normal value in Xcode debug area:
and:
So, I am a little bit confused, what's the deference between (NSInteger)aNumberValue and [aNumberValue integerValue]?
NSNumber is a class; NSInteger is just a typedef of long, which is a primitive type.
dic[#"count"] is a pointer, which means that dic[#"count"] holds an address that points to the NSNumber instance. NSNumber has a method called integerValue which returns an NSInteger as the underlying value that the NSNumber instance represents. So you can conclude that [dic[#"count"] integerValue] gets you a long, and that's how you retrieve the value out of NSNumber.
You don't retrieve the value of NSNumber by casting it to NSInteger. That's because dic[#"count"], as I said, is a pointer. So by writing
NSInteger count = (NSInteger) dic[#"count"];
you are actually casting the pointer itself to an NSInteger, which has nothing to do with the actual represented value. The value 402008592 you see is just a decimal representation of the value of the pointer, which is an address.
The command po is used for printing objects, so lldb will actually try to print out the object at the address of count. That's why you get 2 back using po. You can try p count and you'll get 402008592.
About po count < 100: The expression (count < 100) is evaluated first; since count is really just an NSInteger of 402008592, it will evaluate to false.
The root issue is that Objective-C collection classes can only store Objective-C objects, not primitive types like float, int or NSInteger.
Therefore NSNumber provides a mechanism to store numbers and booleans in object form.
The value 402008592 looks like an address, so it's probably an NSNumber object containing an NSInteger value.
Don't get confused by the NS prefix of NSInteger and NSUInteger; they are still primitive types and not objects like NSNumber.

Difference between (BOOL) and boolValue in iOS

What is the difference between these two lines?
alertObj.AlertAddressed=[[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] boolValue];
alertObj.AlertAddressed=(BOOL)[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)];
I'm getting a different result for these two lines - why?
Thanks in advance.
First one gives you actual bool value.
Second one type cast to BOOL the result of
[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)]
I don't know how good you are at pointers but I try to explain
First of all when you get an NSNumber it is an object, and the value of an object is at first it's pointer (so something like 0x0000af) this is simple an adress in the memory, and this address contains your NSNUmber wich contains the actual value (so let's say a bool information in your example)
It's pretty simple, when you do
(BOOL)alertObj.AlertAddressed=(BOOL)[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)];
what actually happen is the try to cast the 0x0000af part to a bool value... NOT what NSNumber contains (what you actually get depends on the current Pointer of the object when you try this)
alertObj.AlertAddressed=[[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] boolValue];
this is something actually implemented in NSNumber and therefore it completly respects what the object does and gives you the bool saved in your NSNumber, and not a cast from it's pointer
NSNumber is an object, BOOL is a primitive type.
NSNumber is a class that wraps numbers, but you can't use to make operations, if you want you should unwrap calling a specific method. Since NSNumber is an object your variable is a pointer holding a reference to an object, not a value.
The first line is correct, the second is wrong because you are casting a pointer to a bool type.
alertObj.AlertAddressed=[[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] boolValue]; This will convert you value to BOOL
alertObj.AlertAddressed=(BOOL)[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)];This is like casting. Considers [NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] will return bool value.

Verify if an NSString object's text is contained in a collection of strings (a domain)

Rather than a long if statement, what is a more compact and readable way to verify if a string is contained in a collection of possible values? In other words, check if a value is within a domain?
I want to do something like this…
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
BOOL valueFoundInDomain = [domain containsObject:#"elephant"];
But I'm concerned about equality checking with NSString. I want to check the value of the text, not object identity.
The documentation for NSArray says the containsObject method uses the isEqual method. But I cannot find in the documentation for NSString an explanation for its implementation of isEqual. The presence of the isEqualToString method suggests that isEqual may be doing something else. If that something else involves interning of string objects, then experimenting myself may give misleading results, so I'd like a documented answer.
I never use -isEqualToString:, only -isEqual: and it just works as it should! (I do this for years.)
NSString is implementing -isEqual: and it returns YES if the other object is a string and it has the same contents.
In Apples Objective-C documentation, methods that are overridden from a baseclass are often not explicitely documented. But -isEqual: is one of the few methods that is implemented in all foundation classes where it makes sense.
The isEqual method does an additional type check to ensure you are comparing two objects of the same class.
IsEqualToString assumes you are sending a string and will crash if you send a nil or object of another type.
Your code looks good for its use case.
Lets Try Using This
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
NSIndexSet *indexes = [domain indexesOfObjectsWithOptions:NSEnumerationConcurrent passingTest:^BOOL(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return [obj isEqualToString:#"elephant"];
}];
// Where indexes contains matched indexes of array elements
Here isqualToString: Returns a Boolean value that indicates whether a given string is equal to the receiver using a literal Unicode-based comparison.isEquealTo: Returns a Boolean value that indicates whether the receiver and a given object are equal. When you know both objects are strings, isEqualToString: is a faster way to check equality than isEqual:

How to turn UITextField user input into a usable pointer to a dictionary key?

I am trying to take a user input in a UIText field and turn it into a pointer that will access a dictionary key and then return the corresponding value for use in an equation. I feel like I am in the ballpark but I can't figure out how to make it work.
The keys are going to be float values, and the user input is the key itself. I realize that sounds confusing. Here's an example: The user inputs 44.25 in the UITextField. I need this user input to find the 44.25 key in my dictionary and then return the corresponding value associated with that key that will then plug into a simple equation.
Here is my code calling the dictionary (which works) and my attempt at making a pointer out of the input, which does not work.
Thanks in advance for your help.
NSString *path = [[NSBundle mainBundle] pathForResource:#"myDictionary" ofType:#"plist"];
NSDictionary *myDictionary = [NSDictionary dictionaryWithContentsOfFile: path];
float inches = [self.inchesText.text floatValue];
NSLog(#"%.2f", apples);
NSLog(#"There are %# oranges", MyDictionary [apples]);
Just take the text from your text field and use it in a call to ojectForKey. Make sure you code for the (likely) case where the user-entered key can't be found in the dictionary and you get back a nil.
The code is dirt simple, and might look something like this:
NSString *key = self.inchesText.text;
NSString *value = myDictionary[key];
if (value == nil)
//Tell the user they entered an invalid key
else
//Do whatever you need to do with the fetched value.
As far as I know all keys for NSDictionaries need to be objects, not primitives. That being said you could create an object around your primitive with NSNumber objects.
I don't think that would help you though. Since you are reading the NSDictionary straight from file I would assume they are being read into the Dictionary as NSString values (#"44.25" instead of the number 44.25). If that is the case then you could just pass the pointer to the textfield.text to retrieve your desired Dictionary element (assuming the text is equal to one of your dictionary keys in the file).

Resources