I'm using OCMock and I'm trying to do to something like this in one of my tests:
[[mockScrollView expect] setContentSize:[OCMArg any]];
The problem is that [OCMArg any] returns an id type, and I want to use any CGSize, because I don't know it's exact value. How can I pass this argument?
With version 2.2 of OCMock you can use ignoringNonObjectArgs
[[mockScrollView expect] ignoringNonObjectArgs] setContentSize:dummySize];
AFAIK there is no way to accomplish that with OCMock.
An alternative is to create a hand made mock. This is a subclass of UIScrollView where you override setContentSize: assigning the given size to a ivar that later on you can inspect.
Other easier option is to use a real UIScrollView and check directly is the contentSize is the one you expect. I would go for this solution.
Sadly it looks like you'd have to extend OCMock in order to accomplish this. You could follow this pattern...
OCMArg.h
// Add this:
+ (CGSize)anyCGSize;
OCMArg.c
// Add this:
+ (CGSize)anyCGSize
{
return CGSizeMake(0.1245, 5.6789);
}
// Edit this method:
+ (id)resolveSpecialValues:(NSValue *)value
{
const char *type = [value objCType];
// Add this:
if(type[0] == '{')
{
NSString *typeString = [[[NSString alloc] initWithCString:type encoding:NSUTF8StringEncoding] autorelease];
if ([typeString rangeOfString:#"CGSize"].location != NSNotFound)
{
CGSize size = [value CGSizeValue];
if (CGSizeEqualToSize(size, CGSizeMake(0.1245, 5.6789)))
{
return [OCMArg any];
}
}
}
// Existing code stays the same...
if(type[0] == '^')
{
void *pointer = [value pointerValue];
if(pointer == (void *)0x01234567)
return [OCMArg any];
if((pointer != NULL) && (object_getClass((id)pointer) == [OCMPassByRefSetter class]))
return (id)pointer;
}
return value;
}
Related
I am getting a Json from server by making a network request in my app.I am getting <null> value for some keys in Json object.My app gets crashed if this type of response is received.Please tell me how can i validate>?
I have tried this but it does not work all time.
if(!(user_post.username==(id)[NSNull null]) )
{
user_post.username=[dict_user_info objectForKey:#"name"];
if(user_post.username!=nil)
{
ser_post.username=[dict_user_info objectForKey:#"name"];
}
else
{
user_post.username=#"Username";
}
}
Consider testing the value for null so your program won't crash. Like this:
if([dict_user_info objectForKey:#"name"] != [NSNull null])
{
ser_post.username=[dict_user_info objectForKey:#"name"];
}
Create a Category of NSDictionary and add following method in it, which replaces null value with empty string for each key in dictionary.
- (NSDictionary *)dictionaryByReplacingNullsWithStrings
{
const NSMutableDictionary *replaced = [self mutableCopy];
const id nul = [NSNull null];
const NSString *blank = #"";
for(NSString *key in self) {
const id object = [self objectForKey:key];
if(object == nul || object == NULL) {
//pointer comparison is way faster than -isKindOfClass:
//since [NSNull null] is a singleton, they'll all point to the same
//location in memory.
[replaced setObject:blank
forKey:key];
}
}
return [replaced copy];
}
Usage :
[yourJSONDictionary dictionaryByReplacingNullsWithStrings];
Read more about Category in iOS Tutorial 1 and Tutorial 2
yourJsonObject = [myDic valueforkey#"key"];
if(yourJsonObject != [NSNull null])
{
//not null
}
** you can also check whether object exist or not
if(yourJsonObject)
{
//exist
}
I think you've confused your logic. I am trying to stay true to your code, but let me know if the following is not what you intended:
if (dict_user_info[#"name"] != nil && [dict_user_info[#"name"] isKindOfClass:[NSNull class]] == NO) {
user_post.username = dict_user_info[#"name"];
if (user_post.username != nil) {
ser_post.username = user_post.username;
} else {
user_post.username = #"Username";
}
}
These are a couple of methods I wrote for my projects, try them :
/*!
* #brief Makes sure the object is not NSNull or NSCFNumber, if YES, converts them to NSString
* #discussion Sometimes JSON responses can contain NSNull objects, which does not play well with Obj-C. So when you access a value from a JSON and expect it to be an NSString, pass it through this method just to make sure thats the case.
* #param str The object that is supposed to be a string
* #return The object cleaned of unacceptable values
*/
+ (NSString *)cleanedJsonString:(id)str
{
NSString *formattedstr;
formattedstr = (str == [NSNull null]) ? #"" : str;
if ([str isKindOfClass:[NSNumber class]]) {
NSNumber *num = (NSNumber*) str;
formattedstr = [NSString stringWithFormat:#"%#",num];
}
return formattedstr;
}
/*!
* #brief Makes Sure the object is not NSNull
* #param obj Sometimes JSON responses can contain NSNull objects, which does not play well with Obj-C. So when you access a value from a JSON ( NSArray, NSDictionary or NSString), pass it through this method just to make sure thats the case.
* #return The object cleaned of unacceptable values
*/
+ (id)cleanedObject:(id)obj
{
return (obj == [NSNull null]) ? nil : obj;
}
/*!
* #brief A JSON cleaning function for NSArray Objects.
* #discussion Sometimes JSON responses can contain NSNull objects, which does not play well with Obj-C. So when you access a value from a JSON and expect it to be an NSArray, pass it through this method just to make sure thats the case. This method first checks if the object itself is NSNull. If not, then it traverses the array objects and cleans them too.
* #param arr The Objects thats supposed to be an NSArray
* #return The NSNull Cleaned object
*/
+ (NSArray *)cleanedJsonArray:(id)arr
{
if (arr == [NSNull null]) {
return [[NSArray alloc] init];
}
else
{
NSMutableArray *arrM = [(NSArray*)arr mutableCopy];
int i=0;
for (id __strong orb in arrM)
{
if (orb == [NSNull null])
{
[arrM removeObjectAtIndex:i];;
}
i++;
}
return arrM;
}
}
Just pass a JSON string, array or object to the appropriate method and the method will clean it for you.
Do yourself a favour and write a method that handles this and put it into an extension. Like
- (NSString*)jsonStringForKey:(NSString*)key
{
id result = self [key];
if (result == nil || result == [NSNull null]) return nil;
if ([result isKindOfClass:[NSString class]]) return result;
NSLog (#"Key %#: Expected string, got %#", key, result);
return nil;
}
You might even add some code that accepts NSNumber* results and turns them into strings, if that is what your server returns (some poster here had the problem that his server returned dress sizes as numbers like 40 or strings like "40-42" which makes something like this useful).
And then your code becomes one readable line
user_post.username = [dict_user_info jsonStringForKey:#"name"] ?: #"username";
I actually use several slightly different methods depending on whether I expect null, expect no value, expect an empty string or not, which gives me warnings when my assumptions are wrong (but always returns something that doesn't break).
try this:
if(!(user_post.username == (NSString *)[NSNull null]) )
i have googled and came to know that how to use the variable arguments. but i want to pass my variable arguments to another method. i m getting errors. how to do that ?
-(void) aMethod:(NSString *) a, ... {
[self anotherMethod:a];
// i m doing this but getting error. how to pass complete vararg to anotherMethod
}
AFAIK ObjectiveC (just like C and C++) do not provide you with a syntax that allows what you directly have in mind.
The usual workaround is to create two versions of a function. One that may be called directly using ... and another one called by others functions passing the parameters in form of a va_list.
..
[obj aMethod:#"test this %d parameter", 1337);
[obj anotherMethod:#"test that %d parameter", 666);
..
-(void) aMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a withParameters:(va_list)valist
{
NSLog([[[NSString alloc] initWithFormat:a arguments:valist] autorelease]);
}
You cannot pass variadic arguments directly. But some of these methods provide an alternative that you can pass a va_list argument e.g.
#include <stdarg.h>
-(void)printFormat:(NSString*)format, ... {
// Won't work:
// NSString* str = [NSString stringWithFormat:format];
va_list vl;
va_start(vl, format);
NSString* str = [[[NSString alloc] initWithFormat:format arguments:vl] autorelease];
va_end(vl);
printf("%s", [str UTF8String]);
}
Have you considered setting up your arguments in either an array or dictionary, and coding conditionally?
-(void) aMethodWithArguments:(NSArray *)arguments {
for (id *object in arguments) {
if ([object isKindOfClass:fooClass]) {
//handler for objects that are foo
[self anotherMethod:object];
}
if ([object isKindOfClass:barClass]) {
//and so on...
[self yetAnotherMethod:object];
}
}
}
I think you could use macros to achieve same thing.
Let's say you wanna pass aMethod's variable arguments to another
-(void) aMethod:(NSString *) a, ... {
}
You could define your another 'method' using macro though it is not a real method:
#define anotherMethod(_a_,...) [self aMethod:_a_,##__VA_ARGS__]
This is my solution.
I am trying to use the static variable here tagsToCheck, accessible from all static methods in this class.
What is the best way?
A retain here solve the problem, but is it the right way?
tagsToCheck = [#[#"<html>", #"<br>", #"<br />", #"<br/>", #"<p>", #"<div>", #"<b>",
#"<i>", #"<font>", #"<ul>", #"<li>"] retain];
The original code is,
static NSArray * tagsToCheck = nil;
#implementation DictionaryUtil
+ (void) initialize {
tagsToCheck = #[#"<html>", #"<br>", #"<br />", #"<br/>", #"<p>", #"<div>", #"<b>",
#"<i>", #"<font>", #"<ul>", #"<li>"];
}
+ (BOOL) isHtml:(NSString *)string
{
if (!string) return NO;
for (NSString *tag in tagsToCheck) { // bad access here for tagsToCheck
if ([string rangeOfString:tag options:NSCaseInsensitiveSearch].location != NSNotFound) {
return YES;
}
}
return NO;
}
Yes the Objective-C array literal is an autoreleased object, so using retain is correct.
Using retain is not the best way. The best way is to enable Automatic Reference Counting.
SOLVED
It seemed that both -referenceDictionary were not containing exact same information… Dang it ! Fixed it and now it works perfectly.
Initial question
It's been a few hours since I started struggling with this problem, here it is :
I have a CustomClassA which I use as NSDictionary key (obviously for the Object:ForKey: method).
At first it did not work at all, then I read this, that and a few other stuffs around the web…
So I tried overriding both -isEqual: and -hash by doing as said (maybe a little less :p) :
- (BOOL)isEqual:(id)anObject
{
if([anObject isKindOfClass:[self class]])
{
NSLog(#"HASH = %#", [anObject hash] == [self hash] ? #"YES" : #"NO");
NSLog(#"DICT = %#", [[self referenceDictionary] isEqualToDictionary:[anObject referenceDictionary]] ? #"YES" : #"NO");
return [[self referenceDictionary] isEqualToDictionary:[anObject referenceDictionary]];
}
return false;
}
- (NSUInteger)hash
{
NSUInteger prime = 31;
NSUInteger result = 1;
NSArray* values = [referenceDictionary allValues];
for(id k in values)
{
result = prime * result + [k hash];
}
return result;
}
Please, note that my NSDictionary will never contain nil values because I do take care of having these away.
Anyway, both of these methods do return YES on one of the NSDictionary entry when calling :[myDict objectForKey:myObject].
But still, it seems [myDict objectForKey:myObject] returns nil, even after "setting" it myself…
If anyone has time to help me solving my pb I'd be thankful !
Regards,
Cehm
EDIT:
Ok, for the purpose of being a bit more clear about this issue. Lets assume two classes : Artist and Single.
Then I want to create a NSDictionary which contains an Artist as the key and this Artist key points to a NSArray of "Single"s.
For instance I'd have the following dictionary :
#{
artistA : #[song1, song2, song3],
artistB : #[songZ],
artistC : #[songF]
}
But now the thing is I want to create this dictionary like this :
for(Single* aSong in ArrayOfSingle)
{
Artist* anArtist = [[Artist alloc] initWithArtistName:aSong.artistName];
if([data objectForKey: anArtist] == nil)
{
[data setObject:[[NSMutableArray alloc] init] forKey: anArtist];
} else
{
NSLog(#"exists");
}
[[data objectForKey: anArtist] addObject:aSong];
}
Taken into account that data is mutable (Simple NSMutableDictionary) and that I won't ever change anArtist (assuming ARC still keeps its instance…).
EDIT 2:
I overrode NSCopying :
- (id)copyWithZone:(NSZone *)zone {
return [[[self class] allocWithZone:zone] initWithDictionary:referenceDictionary];
}
As my CustomClass uses initWithDictionary: I assumed it would work just fine by doing this.
EDIT 3:
Here's the initWithDictionary:
-(id) initWithDictionary:(NSDictionary*) dictionary
{
self = [super initWithDictionary:dictionary];
if(self)
{
referenceDictionary = dictionary;
title = [super notNullObjectForKey:#"title"];
name = [super notNullObjectForKey:#"name"];
category = [super notNullObjectForKey:#"category"];
artist = [super notNullObjectForKey:#"artist"];
ad_city = [super notNullObjectForKey:#"ad_city"];
}
return self;
}
is there any way in Objective-C to check whether an Object has been created inside a loop (while, for)?
Thanks in advance.
NSString *obj = nil;
while()
{
//Create object
obj = [[NSString alloc] init];
}
//check obj is nil or not
if(nil == obj)
{
// obj not created
}
Yes but for an accurate answer can you tell us what the object is and when it is declared? Can you post the code you have?
int x=0;
while (x<3) {
NSString *i = #"hello world";
x++;
}
NSLog(#"i is %#", i) // i is only declared inside the while loop so not available here
Alternatively,
int x=0;
NSString *i;
while (x<3) {
i = #"hello world";
x++;
}
NSLog(#"i is %#", i); // i is declared beforehand outside the while loop so is available here
If you then need to act on whether it's been created or not use Anil's answer but it's the scope that's important here
I don't think you can know if it was created in a loop, but because you are writing the code where the object will be created in a loop you could call a special init method...
SpecialClass * obj = [[SpecialClass alloc] init];
while (isTrue)
{
SpecialClass * loopObj = [[SpecialClass alloc] initCreatedByLoop];
// Do whats needed
}
and in your SpecialClass you create a specific initialiser...
#implementation SpecialClass
-(id)initCreatedByLoop {
self = [super init];
if (self) {
// What ever you want to do
}
return self;
}