Asked  10 Months ago    Answers:  5   Viewed   189 times

I was trying to create a static variable to store a dictionary of images. Unfortunately, the best way I could find to initialise it was to check in each function that used the variable. Since I am creating this variable inside a category, I can't just initialise it inside the initialiser. Is there a neater way of initialising navigationBarImages?

static NSMutableDictionary *navigationBarImages = NULL;

@implementation UINavigationBar(CustomImage)
//Overrider to draw a custom image
- (void)drawRect:(CGRect)rect
{
    if(navigationBarImages==NULL){
        navigationBarImages=[[NSMutableDictionary alloc] init];
    }
    NSString *imageName=[navigationBarImages objectForKey:self];
    if (imageName==nil) {
        imageName=@"header_bg.png";
    }
    UIImage *image = [UIImage imageNamed: imageName];
    [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}

//Allow the setting of an image for the navigation bar
- (void)setImage:(UIImage*)image
{
    if(navigationBarImages==NULL){
        navigationBarImages=[[NSMutableDictionary alloc] init];
    }
    [navigationBarImages setObject:image forKey:self];
}
@end

 Answers

3
__attribute__((constructor))
static void initialize_navigationBarImages() {
  navigationBarImages = [[NSMutableDictionary alloc] init];
}

__attribute__((destructor))
static void destroy_navigationBarImages() {
  [navigationBarImages release];
}

These function will be called automatically when the program starts and ends.

Sunday, August 8, 2021
 
2

extObjC has the NEATEST stuff you can do with Protocols / Categories... first off is @concreteprotocol...

  • Defines a "concrete protocol," which can provide default implementations of methods within protocol.
  • An @protocol block should exist in a header file, and a corresponding @concreteprotocol block in an implementation file.
  • Any object that declares itself to conform to this protocol will receive its method implementations, but only if no method by the same name already exists.

MyProtocol.h

@protocol MyProtocol 
@required - (void)someRequiredMethod;
@optional - (void)someOptionalMethod;
@concrete - (BOOL)isConcrete;   

MyProtocol.m

 @concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ...

so declaring an object MyDumbObject : NSObject <MyProtocol> will automatically return YES to isConcrete.

Also, they have pcategoryinterface(PROTOCOL,CATEGORY) which "defines the interface for a category named CATEGORY on a protocol PROTOCOL". Protocol categories contain methods that are automatically applied to any class that declares itself to conform to PROTOCOL." There is an accompanying macro you also have to use in your implementation file. See the docs.

Last, but NOT least / not directly related to @protocols is synthesizeAssociation(CLASS, PROPERTY), which "synthesizes a property for a class using associated objects. This is primarily useful for adding properties to a class within a category. PROPERTY must have been declared with @property in the interface of the specified class (or a category upon it), and must be of object type."

So many of the tools in this library open (way-up) the things you can do with ObjC... from multiple inheritance... to well, your imagination is the limit.

Monday, June 28, 2021
 
5

It sounds like what you're after is a mixin: define a series of methods that form the behaviour that you want, and then add that behaviour to only the set of classes that need it.

Here is a strategy I've used to great success in my project EnumeratorKit, which adds Ruby-style block enumeration methods to built-in Cocoa collection classes (in particular EKEnumerable.h and EKEnumerable.m:

  1. Define a protocol that describes the behaviour you want. For method implementations you are going to provide, declare them as @optional.

    @protocol WritableView <NSObject>
    
    - (void)writeText:(NSString *)text;
    
    @optional
    - (void)setTextToDomainOfUrl:(NSString *)text;
    - (void)setTextToIntegerValue:(NSInteger)value;
    - (void)setCapitalizedText:(NSString *)text;
    
    @end
    
  2. Create a class that conforms to that protocol, and implements all the optional methods:

    @interface WritableView : NSObject <WritableView>
    
    @end
    
    @implementation WritableView
    
    - (void)writeText:(NSString *)text
    {
        NSAssert(@"expected -writeText: to be implemented by %@", [self class]);
    }
    
    - (void)setTextToDomainOfUrl:(NSString *)text
    {
        // implementation will call [self writeText:text]
    }
    
    - (void)setTextToIntegerValue:(NSInteger)value
    {
        // implementation will call [self writeText:text]
    }
    
    - (void)setCapitalizedText:(NSString *)text
    {
        // implementation will call [self writeText:text]
    }
    
    @end
    
  3. Create a category on NSObject that can add these methods to any other class at runtime (note that this code doesn't support class methods, only instance methods):

    #import <objc/runtime.h>
    
    @interface NSObject (IncludeWritableView)
    + (void)includeWritableView;
    @end
    
    @implementation
    
    + (void)includeWritableView
    {
        unsigned int methodCount;
        Method *methods = class_copyMethodList([WritableView class], &methodCount);
    
        for (int i = 0; i < methodCount; i++) {
            SEL name = method_getName(methods[i]);
            IMP imp = method_getImplementation(methods[i]);
            const char *types = method_getTypeEncoding(methods[i]);
    
            class_addMethod([self class], name, imp, types);
        }
    
        free(methods);
    }
    
    @end
    

Now in the class where you want to include this behaviour (for example, UILabel):

  1. Adopt the WritableView protocol
  2. Implement the required writeText: instance method
  3. Add this to the top of your implementation:

    @interface UILabel (WritableView) <WritableView>
    
    @end
    
    @implementation UILabel (WritableView)
    
    + (void)load
    {
        [self includeWritableView];
    }
    
    // implementation specific to UILabel
    - (void)writeText:(NSString *)text
    {
        self.text = text;
    }
    
    @end
    

Hope this helps. I've found it a really effective way to implement cross-cutting concerns without having to copy & paste code between multiple categories.

Monday, August 2, 2021
 
Zach
 
3

In Objective-C, you call this "class methods", see here:

@interface MyClass : NSObject

+ (void)aClassMethod;
- (void)anInstanceMethod;

@end

The + is the important thing; you call the method like this: [MyClass aClassMethod];

Monday, August 2, 2021
 
Angolao
 
5

C FAQ.

Saturday, October 23, 2021
 
DasDave
 
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :