For example:
someObject.a.and.b.offset(5)
In objecitive-c, we know that a Class can have properties and methods, how to mix them to implement chainable syntax? How to design?
Have a look at this library: Underscore Library
Actually what it does it to return the same object you are operating on, so you can call more methods on the object (chaining them). Also block properties are used in order to obtain this syntax.
Here is an example from the website:
NSArray *tweets = Underscore.array(results)
// Let's make sure that we only operate on NSDictionaries, you never
// know with these APIs ;-)
.filter(Underscore.isDictionary)
// Remove all tweets that are in English
.reject(^BOOL (NSDictionary *tweet) {
return [tweet[#"iso_language_code"] isEqualToString:#"en"];
})
// Create a simple string representation for every tweet
.map(^NSString *(NSDictionary *tweet) {
NSString *name = tweet[#"from_user_name"];
NSString *text = tweet[#"text"];
return [NSString stringWithFormat:#"%#: %#", name, text];
})
.unwrap;
You might want to look at this SO-Thread aswell.
There is another library shown which implements this behaviour.
Here is my note. For example:
#class ClassB;
#interface ClassA : NSObject
//1. we define some the block properties
#property(nonatomic, readonly) ClassA *(^aaa)(BOOL enable);
#property(nonatomic, readonly) ClassA *(^bbb)(NSString* str);
#property(nonatomic, readonly) ClassB *(^ccc)(NSString* str);
#implement ClassA
//2. we implement these blocks, and remember the type of return value, it's important to chain next block
- (ClassA *(^)(BOOL))aaa
{
return ^(BOOL enable) {
//code
if (enable) {
NSLog(#"ClassA yes");
} else {
NSLog(#"ClassA no");
}
return self;
}
}
- (ClassA *(^)(NSString *))bbb
{
return ^(NSString *str)) {
//code
NSLog(#"%#", str);
return self;
}
}
// Here returns a instance which is kind of ClassB, then we can chain ClassB's block.
// See below .ccc(#"Objective-C").ddd(NO)
- (ClassB * (^)(NSString *))ccc
{
return ^(NSString *str) {
//code
NSLog(#"%#", str);
ClassB* b = [[ClassB alloc] initWithString:ccc];
return b;
}
}
//------------------------------------------
#interface ClassB : NSObject
#property(nonatomic, readonly) ClassB *(^ddd)(BOOL enable);
- (id)initWithString:(NSString *)str;
#implement ClassB
- (ClassB *(^)(BOOL))ddd
{
return ^(BOOL enable) {
//code
if (enable) {
NSLog(#"ClassB yes");
} else {
NSLog(#"ClassB no");
}
return self;
}
}
// At last, we can do it like this------------------------------------------
id a = [ClassA new];
a.aaa(YES).bbb(#"HelloWorld!").ccc(#"Objective-C").ddd(NO)
Related
I have a problem with parsing with my server, specially the variable that I add. it doesn't let me to add it. the error message is "Bad receiver type "Bool"(aka "bool")"
here is my code :
#interface MessagingKeyServerResponse : NSObject <NSCopying>
#property (nonatomic, readonly) NSData *key;
#property (nonatomic, readonly) NSString *keyId;
#property (nonatomic, readonly) NSDate *validityStart;
#property (nonatomic, readonly) NSDate *validityEnd;
#property (nonatomic, readonly) BOOL support_long_messages;
#end
#interface MessagingKeyServerResponse ()
// added support_long_messages for parsing
-(instancetype)initWithKey:(NSData *)key keyId:(NSString *)keyId validityStart:(NSDate *)validityStart validityEnd:(NSDate *)validityEnd support_long_messages:(BOOL)support_long_messages;
#end
NS_ASSUME_NONNULL_END
#implementation MessagingKeyServerResponse
// steve note: added message long characters
-(instancetype)initWithKey:(NSData *)key keyId:(NSString *)keyId validityStart:(NSDate *)validityStart validityEnd:(NSDate *)validityEnd support_long_messages:(BOOL)support_long_messages
{
if (!key) {
[NSException raise:NSInvalidArgumentException format:#"No key"];
return nil;
}
if (!keyId) {
[NSException raise:NSInvalidArgumentException format:#"No key id"];
return nil;
}
if (!validityStart) {
[NSException raise:NSInvalidArgumentException format:#"No validity start"];
return nil;
}
if (!validityEnd) {
[NSException raise:NSInvalidArgumentException format:#"No validity end"];
return nil;
}
if (!support_long_messages) {
[NSException raise:NSInvalidArgumentException format:#"there is no support long Characters"];
return nil;
}
if (!([validityStart compare:validityEnd] == NSOrderedAscending)) {
[NSException raise:NSInvalidArgumentException format:#"Invalid validity range"];
return nil;
}
self = [super init];
if (self) {
_key = [key copy];
_keyId = [keyId copy];
_validityStart = [validityStart copy];
_validityEnd = [validityEnd copy];
_support_long_messages = [support_long_messages copy] ;
if (!_key || !_keyId || !_validityStart || !_validityEnd || !_support_long_messages) {
return nil;
}
}
return self;
}
so the error that I receive from _support_long_messages when I want to assign :
_support_long_messages = [support_long_messages copy] ;
any help appreciate.
Simply
_support_long_messages = support_long_messages;
BOOL is a value type, assignment already creates a copy.
Explicit copy is necessary only for reference types (objects).
I am practicing my Objective C skills and have come across a small issue, although I can't seem to find a straight answer to this issue anywhere I look. In the Apple developer guides I am reading, there is nothing in there telling me how to use a class factory method with multiple parameters (say 3 parameters) and return the initialized object via the overridden init method.
Here I have a simple class called XYZPerson.
#implementation XYZPerson
// Class Factory Method
+ (id)person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
// need to return [ [self alloc] init with the 3 paramaters]; here
// Or some other way to do so..
}
// Overridden init method
- (id)init:(NSString *)firstName with:(NSString *)lastName andWIth:(NSDate *)dateOfBirth {
self = [super init];
if (self) {
_firstName = firstName;
_lastName = lastName;
_dateOfBirth = dateOfBirth;
}
return self;
}
// Use the initialized instance variables in a greeting
- (void)sayHello {
NSLog(#"Hello %# %#", self.firstName, self.lastName);
}
And then in my main I am instantiating an XYZPerson object
XYZPerson *person = [XYZPerson person:#"John" with:#"Doe" andWith:[NSDate date]];
[person sayHello];
Can anybody give me a small pointer on how to do this correctly?
If I understand your question, you want the following:
+ (id)person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
XYZPerson *result = [[self alloc] init:firstName with:lastName andWith:dateOfBirth];
return result;
}
If you aren't using ARC, add an autorelease to the return.
BTW - change the return type of the init method to instancetype instead of id.
#implementation XYZPerson
// Class Factory Method
+ (instanceType ) person:(NSString *)firstName with:(NSString *)lastName andWith:(NSDate *)dateOfBirth {
return [[[self class] alloc]init: firstName with: lastName andWith:dateOfBirth];
}
- (instanceType ) init:(NSString *)firstName with:(NSString *)lastName andWIth:(NSDate *)dateOfBirth {
self = [super init];
if (self) {
_firstName = [firstName copy];
_lastName = [lastName copy];
_dateOfBirth = [dateOfBirth copy];
//nb added copy to each of these, we do not own these objects, they could be lost to us..
/// or you could do this instead..
//assuming you synthesised getters/setters for (strong) properties..
[self setFirstName:firstName];
[self setLastName:lastName];
[self setDateOfBirth: dateOfBirth];
}
return self;
}
I have some problems about the NSMutableSet in Objective-C.
I learnt that the NSSet will compare the two objects' hash code to decide whether they are identical or not.
The problems is, I implemented a class that is subclass of NSObject myself. There is a property NSString *name in that class. What I want to do is when instances of this custom class has the same variable value of "name" , they should be identical, and such identical class should not be duplicated when adding to an NSMutableSet.
So I override the - (NSUInteger)hash function, and the debug shows it returns the same hash for my two instances obj1, obj2 (obj1.name == obj2.name). But when I added obj1, obj2 to an NSMutableSet, the NSMutableSet still contained both obj1, obj2 in it.
I tried two NSString which has the same value, then added them to NSMutableSet, the set will only be one NSString there.
What could be the solution? Thank you for any help!
The custom Class:
Object.h:
#import <Foundation/Foundation.h>
#interface Object : NSObject
#property (retain) NSString *name;
#end
Object.m
#implementation Object
#synthesize name;
-(BOOL)isEqualTo:(id)obj {
return [self.name isEqualToString:[(Object *)obj name]] ? true : false;
}
- (NSUInteger)hash {
return [[self name] hash];
}
#end
and main:
#import <Foundation/Foundation.h>
#import "Object.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Object *obj1 = [[Object alloc]init];
Object *obj2 = [[Object alloc]init];
obj1.name = #"test";
obj2.name = #"test";
NSMutableSet *set = [[NSMutableSet alloc] initWithObjects:obj1, obj2, nil];
NSLog(#"%d", [obj1 isEqualTo:obj2]);
NSLog(#"%ld", [set count]);
}
return 0;
}
Instead of implementing isEqualTo: you have to implement isEqual:
- (BOOL)isEqual:(id)object {
return [object isKindOfClass:[MyObject class]] &&
[self.name isEqual:[(MyObject *)object name]];
}
This will (probably falsely) return NO if both self.name and object.name are nil. If you want to return YES if both properties are nil you should use
- (BOOL)isEqual:(id)object {
if ([object isKindOfClass:[MyObject class]]) {
return (!self.name && ![(MyObject *)object name]) ||
[self.name isEqual:[(MyObject *)object name]];
}
return NO;
}
I tried to create a singleton to set and get a string between different views:
globalVar.h:
#interface globalVar : NSObject
{
NSString *storeID;
}
+ (globalVar *)sharedInstance;
#property (nonatomic, copy) NSString *storeID;
#end
globalVar.m:
#import "globalVar.h"
#implementation globalVar
#synthesize storeID;
+ (globalVar *)sharedInstance
{
static globalVar *myInstance = nil;
if (nil == myInstance) {
myInstance = [[[self class] alloc] init];
}
return myInstance;
}
#end
Now how do I actually use the string? Say I want to set it to "asdf" in one view and load the "asdf" in another view.
To set it, do something like:
[globalVar sharedInstance].storeID = #"asdf";
And to use it:
NSString *myString = [globalVar sharedInstance].storeID;
First, you need to change how you create your instance. Do it like this:
+ (GlobalVar *)sharedInstance
{
static GlobalVar *myInstance;
#synchronized(self) {
if (nil == myInstance) {
myInstance = [[self alloc] init];
}
}
return myInstance;
}
You do not want to use [self class], because in this case self is already the globalVar class.
Second, you should name the class GlobalVar with a capital G.
Third, you will use it like this:
[GlobalVar sharedInstance].storeID = #"STORE123";
NSLog(#"store ID = %#", [GlobalVar sharedInstance].storeID);
singleton.h
#import <Foundation/Foundation.h>
#interface CrestronControllerValues : NSObject {
NSString* ipAddress;
NSString* portNumber;
NSString* phoneAddress;
NSString* cameleonVersion;
NSString* systemName;
NSString* iPID;
NSString* systemFeedBackName;
NSString* dJoinConnectedFB;
NSString* dJoinLow;
NSString* dJoinHigh;
NSString* aJoinLow;
NSString* aJoinHigh;
NSString* sJoinLow;
NSString* sJoinHigh;
NSMutableArray *currentPhonebookEntriesTelepresence;
NSMutableArray *currentPhonebookEntriesVideoChat;
NSMutableArray *currentPhonebookEntriesAudioChat;
}
#property (nonatomic, retain) NSString* ipAddress;
#property (nonatomic, retain) NSString* portNumber;
#property (nonatomic, retain) NSString* phoneAddress;
#property (nonatomic, retain) NSString* cameleonVersion;
#property (nonatomic, retain) NSMutableArray *currentPhonebookEntriesTelepresence;
#property (nonatomic, retain) NSMutableArray *currentPhonebookEntriesVideoChat;
#property (nonatomic, retain) NSMutableArray *currentPhonebookEntriesAudioChat;
#property (nonatomic, retain) NSString* systemName;
#property (nonatomic, retain) NSString* iPID;
#property (nonatomic, retain) NSString* systemFeedBackName;
#property (nonatomic, retain) NSString* dJoinConnectedFB;
#property (nonatomic, retain) NSString* dJoinLow;
#property (nonatomic, retain) NSString* dJoinHigh;
#property (nonatomic, retain) NSString* aJoinLow;
#property (nonatomic, retain) NSString* aJoinHigh;
#property (nonatomic, retain) NSString* sJoinLow;
#property (nonatomic, retain) NSString* sJoinHigh;
+ (id)sharedManager;
#end
i have my singleton.m:
static CrestronControllerValues *sharedMyManager= nil;
#implementation CrestronControllerValues
#synthesize ipAddress, portNumber ,systemName, iPID, systemFeedBackName, dJoinConnectedFB, dJoinLow, dJoinHigh, aJoinLow, aJoinHigh, sJoinLow, sJoinHigh, cameleonVersion, currentPhonebookEntriesAudioChat, currentPhonebookEntriesTelepresence, currentPhonebookEntriesVideoChat, phoneAddress;
+(CrestronControllerValues*)sharedManager
{
#synchronized(self) {
if(!sharedMyManager) {
sharedMyManager = [CrestronControllerValues alloc];
sharedMyManager = [sharedMyManager init];
}
}
}
+(id)alloc
{
#synchronized(self)
{
NSAssert(sharedMyManager == nil, #"Attempted to allocate a second instance of a singleton.");
sharedMyManager = [super alloc];
return sharedMyManager;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
self.ipAddress = #"10.8.40.64";
self.portNumber = 41794;
self.systemName = #"";
self.iPID = 3;
self.cameleonVersion = nil;
self.currentPhonebookEntriesAudioChat = [[NSMutableArray alloc]initWithObjects:nil];
self.currentPhonebookEntriesTelepresence = [[NSMutableArray alloc]initWithObjects:nil];
self.currentPhonebookEntriesVideoChat = [[NSMutableArray alloc]initWithObjects:nil];
self.phoneAddress = nil;
self.systemFeedBackName = #"";
self.dJoinConnectedFB = 5000;
self.dJoinLow = 1;
self.dJoinHigh = 1000;
self.aJoinLow = 1;
self.aJoinHigh = 1000;
self.sJoinLow = 1;
self.sJoinHigh = 1000;
}
return self;
}
return self;
}
-(void)setPhoneAddress:(NSString *)phoneaddress
{
#synchronized(self) {
if (phoneAddress != phoneaddress)
{
[phoneAddress release];
phoneAddress = [phoneaddress retain];
}
}
}
-(NSString*)getPhoneAddress
{
return phoneAddress;
}
-(void)setCurrentPhonebookEntriesAudioChat:(NSMutableArray *)entries
{
#synchronized(self) {
if (currentPhonebookEntriesAudioChat != entries)
{
[currentPhonebookEntriesAudioChat release];
currentPhonebookEntriesAudioChat = [entries retain];
}
}
}
-(NSMutableArray*)getCurrentPhonebookEntriesAudioChat
{
return currentPhonebookEntriesAudioChat;
}
-(void)setCurrentPhonebookEntriesTelepresence:(NSMutableArray *)entries
{
#synchronized(self) {
if (currentPhonebookEntriesTelepresence != entries)
{
[currentPhonebookEntriesTelepresence release];
currentPhonebookEntriesTelepresence = [entries retain];
}
}
}
-(NSMutableArray*)getCurrentPhonebookEntriesTelepresence
{
return currentPhonebookEntriesTelepresence;
}
-(void)setCurrentPhonebookEntriesVideoChat:(NSMutableArray *)entries
{
#synchronized(self) {
if (currentPhonebookEntriesVideoChat != entries)
{
[currentPhonebookEntriesVideoChat release];
currentPhonebookEntriesVideoChat = [entries retain];
}
}
}
-(NSMutableArray*)getCurrentPhonebookEntriesVideoChatLocal
{
return currentPhonebookEntriesVideoChat;
}
-(void)setCameleonVersion:(NSString *)cameleonversion
{
cameleonVersion = cameleonversion;
}
-(NSString*)getCameleonVersion
{
return cameleonVersion;
}
-(void)setIPaddress:(NSString *)ipaddress
{
ipAddress = ipaddress;
}
-(NSString*)getIPaddress
{
return ipAddress;
}
-(void)setPortNumber:(NSString *)portnumber
{
portNumber = portnumber;
}
-(NSString*)getPortNumber
{
return portNumber;
}
-(void)setSystemName:(NSString *)systemname
{
systemName = systemname;
}
-(NSString*)getSystemName
{
return systemName;
}
-(void)setIPID:(NSString *)ipid
{
iPID=ipid;
}
-(NSString*)getIpid
{
return iPID;
}
-(void)setSystemFeedBackName:(NSString *)systemfeedbackname
{
systemFeedBackName=systemfeedbackname;
}
-(NSString*)getSystemFeedBackName
{
return systemFeedBackName;
}
-(void)setDJoinConnectedFB:(NSString *)djoinconnectedfb
{
dJoinConnectedFB = djoinconnectedfb;
}
-(NSString*)getDJoinConnectedFB
{
return dJoinConnectedFB;
}
-(void)setDJoinLow:(NSString *)djoinlow
{
dJoinLow=djoinlow;
}
-(NSString*)getDJoinLow
{
return dJoinLow;
}
-(void)setDJoinHigh:(NSString *)djoinhigh
{
dJoinHigh = djoinhigh;
}
-(NSString*)getDJoinHigh
{
return dJoinHigh;
}
-(void)setAJoinLow:(NSString *)ajoinlow
{
aJoinLow = ajoinlow;
}
-(NSString*)getAJoinLow
{
return aJoinLow;
}
-(void)setAJoinHigh:(NSString *)ajoinhigh
{
aJoinHigh = ajoinhigh;
}
-(NSString*)getAJoinHigh
{
return aJoinHigh;
}
-(void)setSJoinLow:(NSString *)sjoinlow
{
sJoinLow = sjoinlow;
}
-(NSString*)getSJoinLow
{
return sJoinLow;
}
-(void)setSJoinHigh:(NSString *)sjoinhigh
{
sJoinHigh = sjoinhigh;
}
-(NSString*)getSJoinHigh
{
return sJoinHigh;
}
- (void)dealloc
{
[self.ipAddress release];
[self.iPID release];
[self.portNumber release];
[self.currentPhonebookEntriesVideoChat release];
[self.currentPhonebookEntriesTelepresence release];
[self.currentPhonebookEntriesAudioChat release];
[self.aJoinHigh release];
[self.aJoinLow release];
[self.cameleonVersion release];
[self.sJoinHigh release];
[self.sJoinLow release];
[self.dJoinHigh release];
[self.dJoinLow release];
[self.dJoinConnectedFB release];
[super dealloc];
}
#end
and then i use it in 3 classes total
in one i set values:
if i read values from the CCV (sharedobject) i get the correct values. but this is in the same class as they are set from
CCV = [CrestronControllerValues sharedManager];
CCV.currentPhonebookEntriesAudioChat = currentPhonebookEntriesAudioChat;
and another i read the values:
(these show/read as nil)
switch (viewOptions) {
case 1:
[self setTableArray:CCV.currentPhonebookEntriesVideoChat];
break;
case 2:
[self setTableArray:CCV.currentPhonebookEntriesVideoChat];
break;
case 3:
[self setTableArray:CCV.currentPhonebookEntriesTelepresence];
break;
case 4:
[self setTableArray:CCV.currentPhonebookEntriesAudioChat];
break;
default:
[self setTableArray:CCV.currentPhonebookEntriesVideoChat];
break;
}
but besides the class that i actually set the values in i do not get the filled array when i access it from another class
i have done NSLOG(#"%#", CCV) and from what i can see all three classes have the same pointer so the shared instance seems to be working
Here is a simplier singleton pattern, less code is more:
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
_sharedSingleton = [[MySingleton alloc] init];
}
return _sharedMySingleton;
}
sharedMyManager has not been set at the time you are initializing the ivars.
In a init it is best practice to set the ivars directly, that is do not use setters such as created by #synthesize, the class is not completely established so calling methods on it is not a great idea.
A singleton is just a class like any other class with one exception, there is only one. Also all the extra methods to guarantee a singleton are really just noise that is best not present--but that is a matter of taste.
Consider:
sharedMyManager = [[super allocWithZone:NULL] init];
Rewrite it as:
id x = [super allocWithZone:NULL];
id y = [x init];
sharedMyManager = y;
When init is executed, the assignment to sharedMyManager hasn't been evaluated yet. Thus, sharedMyManager is nil and all your assignments are no-ops in your init method.
In your init method, you should always refer to your instance variables through self; either by directly assignment to them (which is a reference to self, really) or using the setter methods directly (i.e. self.foo = 442;).
(This is what #CocoaFu said, but clarified)
Looking at the code a little more closely, there are a ton of problems with it.
NSString properties should be copy, not retain.
you are leaking all of the currentPhonebookEntries* mutable arrays.
Getter methods should not have the prefix get*
there is no need to implement any of those getter/setter methods when using #synthesize (and you are actually creating two getter methods for each; one with and one without the get prefix).
the dealloc method should either directly release the instance variables or it should set the properties to nil; the [self.ivar release] is discouraged.
The code I showed above is merely illustrative. If your init still assigns through sharedMyManager, you didn't fix the problem.
so in the end all i can do is apologize. none of you had the code that you would have needed to see what was going on.
here is the array being saved (aboved was abridged (bad idea))
if ([phonebookEntriesAudioChat count] >=8) {
[CCV setCurrentPhonebookEntriesAudioChat:phonebookEntriesAudioChat];
[phonebookEntriesAudioChat removeAllObjects];
}
basically i was tring to add an item to the array from a socket return. getting one address up to 8 for each return/message. so i populated a temporary array (phonebookEntriesAudioChat) and added one to it for each message and once it got to 8 saved it to my singleton (CCV). but some how (and im still trying to figure this out) it would get to 8, be saved, temporary array cleared, then resaved the array (an empty one) to the singleton.
thanks for all the help and direction, i know i dont get points for my own answer if one of you wants some easy points just re answer with a simliar description as this and ill give u the check. otherwise im just going to vote up ur comments and mark this as the answer in a day or two.