samhuri.net-ios/Pods/libextobjc/extobjc/EXTSafeCategory.h
2014-10-18 14:19:51 -07:00

99 lines
4.2 KiB
Objective-C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// EXTSafeCategory.h
// extobjc
//
// Created by Justin Spahr-Summers on 2010-11-13.
// Copyright (C) 2012 Justin Spahr-Summers.
// Released under the MIT license.
//
#import <objc/runtime.h>
#import "metamacros.h"
/**
* \@safecategory defines the implementation of a safe category named \a CATEGORY on \a CLASS.
* A safe category will only add methods to \a CLASS if a method by the same
* name does not already exist (\e not including superclasses). If \c DEBUG is
* defined and \c NDEBUG is not defined, any method conflicts will abort the
* application at startup; otherwise, any method conflicts are simply logged,
* and no overwriting occurs.
*
* This macro should be used in implementation files. Normal \@interface blocks
* can be used in header files to declare safe categories, as long as the name
* given in parentheses matches \a CATEGORY.
*
* To perform tasks when a safe category is loaded, use the \c +load method,
* which functions identically to \c +load within a regular category. Any \c
* +load method will be executed exactly once, and is not added to \a CLASS. \a
* CLASS is guaranteed to have been loaded by the time \c +load executes.
*
* @note Depending on the protection level for the instance variables of \a
* CLASS, a safe category may not be able to access them, even if a regular
* category could. In practice, accessing instance variables in a category is
* almost always a bad idea.
*
* @warning Protocol categories and safe categories interact in indeterminate
* ways. If \a CLASS conforms to a protocol which has an EXTProtocolCategory,
* and both the protocol category and the safe category implement a method by
* the same name, the safe category may succeed sometimes and fail others.
*
* @bug Due to an implementation detail, methods invoked against \c super will
* actually be invoked against \c self.
*/
#define safecategory(CLASS, CATEGORY) \
/*
* create a class used to contain all the methods used in this category by
* doing this, we can control and fine-tune the method injection process
*/ \
interface CLASS ## _ ## CATEGORY ## _MethodContainer : CLASS {} \
@end \
\
@implementation CLASS ## _ ## CATEGORY ## _MethodContainer \
/*
* using the "constructor" function attribute, we can ensure that this
* function is executed only AFTER all the Objective-C runtime setup (i.e.,
* after all +load methods have been executed)
*/ \
__attribute__((constructor)) \
static void ext_ ## CLASS ## _ ## CATEGORY ## _inject (void) { \
/*
* use this injection point to load the methods into the target class
* this is guaranteed to execute after any regular categories have
* loaded already (though the interaction with EXTProtocolCategory is
* undefined)
*/ \
const char *className_ = metamacro_stringify(CLASS ## _ ## CATEGORY ## _MethodContainer); \
\
/*
* get this class, and the class that is the target of injection
*/ \
Class methodContainer_ = objc_getClass(className_); \
Class targetClass_ = class_getSuperclass(methodContainer_); \
\
/*
* if this method returns NO, we assume that one or more of the category
* methods already existed on the target class, and therefore error out
* (using ext_safeCategoryFailed)
*/ \
if (!ext_loadSafeCategory(methodContainer_, targetClass_)) {\
ext_safeCategoryFailed(CLASS, CATEGORY); \
} \
}
/*** implementation details follow ***/
BOOL ext_loadSafeCategory (Class methodContainer, Class targetClass);
// if this is a debug build...
#if defined(DEBUG) && !defined(NDEBUG)
// abort if a safe category fails to load
#define ext_safeCategoryFailed(CLASS, CATEGORY) \
do { \
fprintf(stderr, "ERROR: Failed to fully load safe category %s (%s)\n", metamacro_stringify(CLASS), metamacro_stringify(CATEGORY)); \
abort(); \
} while (0)
#else
// otherwise, just print an error message
#define ext_safeCategoryFailed(CLASS, CATEGORY) \
fprintf(stderr, "ERROR: Failed to fully load safe category %s (%s)\n", metamacro_stringify(CLASS), metamacro_stringify(CATEGORY))
#endif