[iOS]KVC简单使用

TO:
iOS开发技巧系列---详解KVC(我告诉你KVC的一切)
DLIntrospection使用
https://github.com/delebedev/DLIntrospection
IOS 高级开发 KVC(一)
IOS 高级开发 KVC(二)
iOS runtime消息转发机制等

看了一些相关文章,还是自己写个备份便以后查询。
很多对象被Apple封装后并没提供太多的属性供开发者使用,比如UITextField,但我们可以通过KVC的方式来修改UITextField的占位文字颜色和字体。

    // 修改占位字符的颜色
    [_textField setValue:[UIColor greenColor] forKeyPath:@"_placeholderLabel.textColor"];
    // 修改占位字符的字体
    [_textField setValue:[UIFont boldSystemFontOfSize:18] forKeyPath:@"_placeholderLabel.font"];
    // 文字偏移量
    _textField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 0)];
    // 设置显示模式为永远显示(默认不显示)
    _textField.leftViewMode = UITextFieldViewModeAlways;

效果如下。更多的修改方式可以参考iOS-改变UITextField的占位文字颜色(三种)

[iOS]KVC简单使用_第1张图片

当不知道属性名时,推荐使用“NSObject+DLIntrospection”来打印出UITextField的内部变量,这样可以找到原来是“UITextFieldLabel* _placeholderLabel”。

    NSArray *tempClasses = [UITextField classes];
    NSLog(@"// 打印所有类\n%@",tempClasses);
    NSArray *tempProperties = [UITextField properties];
    NSLog(@"// 打印属性\n%@",tempProperties);
    NSArray *tempInstanceVariables = [UITextField instanceVariables];
    NSLog(@"// 内部变量\n%@",tempInstanceVariables);
    NSArray *tempClassMethods = [UITextField classMethods];
    NSLog(@"// 类方法\n%@",tempClassMethods);
    NSArray *tempInstanceMethods = [UITextField instanceMethods];
    NSLog(@"// 方法\n%@",tempInstanceMethods);
    NSArray *tempProtocols = [UITextField protocols];
    NSLog(@"// 遵守的协议\n%@",tempProtocols);
    NSDictionary *tempDescriptionForProtocol = [UITextField descriptionForProtocol:@protocol(UITextFieldDelegate)];
    NSLog(@"// 自己的协议描述\n%@",tempDescriptionForProtocol);
    NSString *tempParentClassHierarchy = [UITextField parentClassHierarchy];
    NSLog(@"// 层级关系\n%@",tempParentClassHierarchy);

输出:
这里将每一项都做了输出,主要是想看一下结果会是什么样子。

// 打印所有类
(
    AAAbsintheContext,
    AAAbsintheSigner,
    AAAbsintheSignerContextCache,
    AAAccount,
    AAAccountManagementUIResponse,
    AAAccountManager,
    AAAddEmailUIRequest,
    AAAppleIDSettingsRequest,
    AAAppleTVRequest,
    AAAttestationSigner,
    AAAuthenticateRequest,
    AAAuthenticationResponse,
    AAAutoAccountVerifier,
    AAAvailabilityRequest,
    AAAvailabilityResponse,
    AACertificatePinner,
    AAChildAccountCreationUIRequest,
    AACloudKitDevicesListRequest,
    AACloudKitDevicesListResponse,
    AACloudKitMigrationStateRequest,
    AACloudKitMigrationStateResponse,
    AACloudKitStartMigrationRequest,
    AACloudKitStartMigrationResponse,
    AACompleteEmailVettingRequest,
    AACompleteEmailVettingResponse,
    AADataclassManager,
    AADelegateAccountSetupHelper,
    AADevice,
    AADeviceInfo,
    AADeviceListRequest,
    AADeviceListResponse,
    AADeviceProvisioningRequest,
    AADeviceProvisioningResponse,
    AADeviceProvisioningSession,
    AAEmailVettin
    ...共14159个...
)

// 打印属性
<__NSFrozenArrayM 0x60000300fe10>(
    @property (atomic, assign, readonly) unsigned long hash,
    @property (atomic, assign, readonly) class superclass,
    @property (atomic, copy, readonly) NSString* description,
    @property (atomic, copy, readonly) NSString* debugDescription,
    @property (atomic, assign, readonly) unsigned long hash,
    @property (atomic, assign, readonly) class superclass,
    @property (atomic, copy, readonly) NSString* description,
    @property (atomic, copy, readonly) NSString* debugDescription,
    @property (nonatomic, copy) NSString* ab_text,
    @property (nonatomic, copy) NSDictionary* ab_textAttributes,
    @property (nonatomic, strong, setter=(null)) NSLayoutConstraint* _baselineLayoutConstraint,
    @property (nonatomic, strong, setter=(null)) _UIBaselineLayoutStrut* _baselineLayoutLabel,
    @property (nonatomic, assign) BOOL _tvUseVibrancy,
    @property (nonatomic, strong) UIColor* _tvCustomTextColor,
    @property (nonatomic, strong) UIColor* _tvCustomFocusedTextColor,
    @property (nonatomic, assign) BOOL _disableTextColorUpdateOnTraitCollectionChange,
    @property (nonatomic, strong) _UITextFieldVisualStyle* visualStyle,
    @property (atomic, strong) UIView* recentsAccessoryView,
    @property (nonatomic, copy) NSString* text,
    @property (nonatomic, copy) NSAttributedString* attributedText,
    @property (nonatomic, strong) UIColor* textColor,
    @property (nonatomic, strong) UIFont* font,
    @property (nonatomic, assign) long textAlignment,
    @property (nonatomic, assign) long borderStyle,
    @property (nonatomic, copy) NSDictionary* defaultTextAttributes,
    @property (nonatomic, copy) NSString* placeholder,
    @property (nonatomic, copy) NSAttributedString* attributedPlaceholder,
    @property (nonatomic, assign) BOOL clearsOnBeginEditing,
    @property (nonatomic, assign) BOOL adjustsFontSizeToFitWidth,
    @property (nonatomic, assign) double minimumFontSize,
    @property (nonatomic, weak) * delegate,
    @property (nonatomic, strong) UIImage* background,
    @property (nonatomic, strong) UIImage* disabledBackground,
    @property (nonatomic, assign, readonly, getter=isEditing) BOOL editing,
    @property (nonatomic, assign) BOOL allowsEditingTextAttributes,
    @property (nonatomic, copy) NSDictionary* typingAttributes,
    @property (nonatomic, assign) long clearButtonMode,
    @property (nonatomic, strong) UIView* leftView,
    @property (nonatomic, assign) long leftViewMode,
    @property (nonatomic, strong) UIView* rightView,
    @property (nonatomic, assign) long rightViewMode,
    @property (atomic, strong) UIView* inputView,
    @property (atomic, strong) UIView* inputAccessoryView,
    @property (nonatomic, assign) BOOL clearsOnInsertion,
    @property (atomic, assign, readonly) unsigned long hash,
    @property (atomic, assign, readonly) class superclass,
    @property (atomic, copy, readonly) NSString* description,
    @property (atomic, copy, readonly) NSString* debugDescription,
    @property (nonatomic, assign) long autocapitalizationType,
    @property (nonatomic, assign) long autocorrectionType,
    @property (nonatomic, assign) long spellCheckingType,
    @property (nonatomic, assign) long smartQuotesType,
    @property (nonatomic, assign) long smartDashesType,
    @property (nonatomic, assign) long smartInsertDeleteType,
    @property (nonatomic, assign) long keyboardType,
    @property (nonatomic, assign) long keyboardAppearance,
    @property (nonatomic, assign) long returnKeyType,
    @property (nonatomic, assign) BOOL enablesReturnKeyAutomatically,
    @property (nonatomic, assign, getter=isSecureTextEntry) BOOL secureTextEntry,
    @property (nonatomic, copy) NSString* textContentType,
    @property (nonatomic, copy) UITextInputPasswordRules* passwordRules,
    @property (nonatomic, assign, readonly) BOOL hasText,
    @property (nonatomic, copy) NSString* recentInputIdentifier,
    @property (nonatomic, assign) {_NSRange=QQ} validTextRange,
    @property (nonatomic, copy) NSIndexSet* PINEntrySeparatorIndexes,
    @property (nonatomic, assign) {__CFCharacterSet=} * textTrimmingSet,
    @property (nonatomic, strong) UIColor* insertionPointColor,
    @property (nonatomic, strong) UIColor* selectionBarColor,
    @property (nonatomic, strong) UIColor* selectionHighlightColor,
    @property (nonatomic, strong) UIImage* selectionDragDotImage,
    @property (nonatomic, strong) UIColor* underlineColorForTextAlternatives,
    @property (nonatomic, strong) UIColor* underlineColorForSpelling,
    @property (nonatomic, assign) unsigned long insertionPointWidth,
    @property (nonatomic, assign) int textLoupeVisibility,
    @property (nonatomic, assign) int textSelectionBehavior,
    @property (nonatomic, assign) id textSuggestionDelegate,
    @property (nonatomic, assign) BOOL isSingleLineDocument,
    @property (nonatomic, assign) BOOL contentsIsSingleValue,
    @property (nonatomic, assign) BOOL hasDefaultContents,
    @property (nonatomic, assign) BOOL acceptsPayloads,
    @property (nonatomic, assign) BOOL acceptsEmoji,
    @property (nonatomic, assign) BOOL acceptsDictationSearchResults,
    @property (nonatomic, assign) BOOL forceEnableDictation,
    @property (nonatomic, assign) BOOL forceDisableDictation,
    @property (nonatomic, assign) BOOL forceDefaultDictationInfo,
    @property (nonatomic, assign) long forceDictationKeyboardType,
    @property (nonatomic, assign) int emptyContentReturnKeyType,
    @property (nonatomic, assign) BOOL returnKeyGoesToNextResponder,
    @property (nonatomic, assign) BOOL acceptsFloatingKeyboard,
    @property (nonatomic, assign) BOOL acceptsSplitKeyboard,
    @property (nonatomic, assign) BOOL displaySecureTextUsingPlainText,
    @property (nonatomic, assign) BOOL displaySecureEditsUsingPlainText,
    @property (nonatomic, assign) BOOL learnsCorrections,
    @property (nonatomic, assign) int shortcutConversionType,
    @property (nonatomic, assign) BOOL suppressReturnKeyStyling,
    @property (nonatomic, assign) BOOL useInterfaceLanguageForLocalization,
    @property (nonatomic, assign) BOOL deferBecomingResponder,
    @property (nonatomic, assign) BOOL enablesReturnKeyOnNonWhiteSpaceContent,
    @property (nonatomic, copy) NSString* autocorrectionContext,
    @property (nonatomic, copy) NSString* responseContext,
    @property (nonatomic, strong) UIInputContextHistory* inputContextHistory,
    @property (nonatomic, assign) BOOL disableInputBars,
    @property (nonatomic, assign) BOOL isCarPlayIdiom,
    @property (nonatomic, assign) long textScriptType,
    @property (nonatomic, assign) BOOL loadKeyboardsForSiriLanguage,
    @property (nonatomic, assign) BOOL disablePrediction,
    @property (nonatomic, assign) BOOL hidePrediction,
    @property (nonatomic, assign, getter=isDevicePasscodeEntry) BOOL devicePasscodeEntry,
    @property (nonatomic, assign, readonly) NSLayoutManager* layoutManager,
    @property (nonatomic, assign, readonly) NSTextStorage* textStorage,
    @property (nonatomic, assign, readonly) NSTextContainer* textContainer,
    @property (nonatomic, assign) long nonEditingLinebreakMode,
    @property (nonatomic, assign) BOOL allowsAttachments,
    @property (atomic, copy) UITextRange* selectedTextRange,
    @property (nonatomic, assign, readonly) UITextRange* markedTextRange,
    @property (nonatomic, copy) NSDictionary* markedTextStyle,
    @property (nonatomic, assign, readonly) UITextPosition* beginningOfDocument,
    @property (nonatomic, assign, readonly) UITextPosition* endOfDocument,
    @property (nonatomic, weak) * inputDelegate,
    @property (nonatomic, assign, readonly) * tokenizer,
    @property (nonatomic, assign, readonly) UIView* textInputView,
    @property (nonatomic, assign) long selectionAffinity,
    @property (nonatomic, assign, readonly) id insertDictationResultPlaceholder,
    @property (nonatomic, weak) * textDragDelegate,
    @property (nonatomic, assign, readonly) UIDragInteraction* textDragInteraction,
    @property (nonatomic, assign, readonly, getter=isTextDragActive) BOOL textDragActive,
    @property (nonatomic, assign) long textDragOptions,
    @property (nonatomic, copy) UIPasteConfiguration* pasteConfiguration,
    @property (nonatomic, weak) * pasteDelegate,
    @property (nonatomic, weak) * textDropDelegate,
    @property (nonatomic, assign, readonly) UIDropInteraction* textDropInteraction,
    @property (nonatomic, assign, readonly, getter=isTextDropActive) BOOL textDropActive,
    @property (nonatomic, assign) {CGPoint=dd} contentOffsetForSameViewDrops,
    @property (nonatomic, assign) BOOL adjustsFontForContentSizeCategory
)

// 内部变量
<__NSFrozenArrayM 0x6000030fdaa0>(
    long _borderStyle,
    double _minimumFontSize,
    id _delegate,
    UIImage* _background,
    UIImage* _disabledBackground,
    long _clearButtonMode,
    UIView* _leftView,
    long _leftViewMode,
    UIView* _rightView,
    long _rightViewMode,
    UIView* _contentCoverView,
    long _contentCoverViewMode,
    UIView* _backgroundCoverView,
    long _backgroundCoverViewMode,
    UITextInputTraits* _traits,
    UITextInputTraits* _nonAtomTraits,
    _UIFullFontSize* _fullFontSize,
    {UIEdgeInsets="top"d"left"d"bottom"d"right"d} _padding,
    float _progress,
    UIButton* _clearButton,
    {CGSize="width"d"height"d} _clearButtonOffset,
    {CGSize="width"d"height"d} _leftViewOffset,
    {CGSize="width"d"height"d} _rightViewOffset,
    UITextFieldBorderView* _backgroundView,
    UITextFieldBorderView* _disabledBackgroundView,
    UITextFieldBackgroundView* _systemBackgroundView,
    _UITextFieldContentView* _textContentView,
    _UIFloatingContentView* _floatingContentView,
    UIVisualEffectView* _contentBackdropView,
    _UIDetachedFieldEditorBackgroundView* _fieldEditorBackgroundView,
    UIVisualEffectView* _fieldEditorEffectView,
    UITextFieldLabel* _placeholderLabel,
    UITextFieldLabel* _suffixLabel,
    UITextFieldLabel* _prefixLabel,
    UIImageView* _iconView,
    UILabel* _label,
    double _labelOffset,
    NSAttributedString* _overriddenPlaceholder,
    long _overriddenPlaceholderAlignment,
    UITextInteractionAssistant* _interactionAssistant,
    UITapGestureRecognizer* _selectGestureRecognizer,
    UIFieldEditor* _fieldEditor,
    NSTextContainer* __textContainer,
    _UIFieldEditorLayoutManager* __layoutManager,
    _UICascadingTextStorage* _textStorage,
    UITextPasteController* _pasteController,
    UIView* _inputView,
    UIView* _inputAccessoryView,
    UIView* _recentsAccessoryView,
    UISystemInputViewController* _systemInputViewController,
    UITextFieldAtomBackgroundView* _atomBackgroundView,
    * _textDragDropSupport,
    {?="verticallyCenterText"b1"isAnimating"b4"inactiveHasDimAppearance"b1"becomesFirstResponderOnClearButtonTap"b1"clearsPlaceholderOnBeginEditing"b1"adjustsFontSizeToFitWidth"b1"fieldEditorAttached"b1"canBecomeFirstResponder"b1"shouldSuppressShouldBeginEditing"b1"inResignFirstResponder"b1"undoDisabled"b1"explicitAlignment"b1"implementsCustomDrawing"b1"needsClearing"b1"suppressContentChangedNotification"b1"allowsEditingTextAttributes"b1"usesAttributedText"b1"backgroundViewState"b2"clearingBehavior"b2"overridePasscodeStyle"b1"shouldResignWithoutUpdate"b1"blurEnabled"b1"disableFocus"b1"disableRemoteTextEditing"b1"allowsAttachments"b1"contentCoverUnsecuresText"b1"forcesClearButtonHighContrastAppearance"b1} _textFieldFlags,
    BOOL _deferringBecomeFirstResponder,
    BOOL _animateNextHighlightChange,
    CUICatalog* _cuiCatalog,
    CUIStyleEffectConfiguration* _cuiStyleEffectConfiguration,
    double _roundedRectBackgroundCornerRadius,
    NSArray* _overriddenAttributesForEditing,
    BOOL _adjustsFontForContentSizeCategory,
    BOOL _tvUseVibrancy,
    BOOL _disableTextColorUpdateOnTraitCollectionChange,
    * _pasteDelegate,
    NSLayoutConstraint* _baselineLayoutConstraint,
    _UIBaselineLayoutStrut* _baselineLayoutLabel,
    UIColor* _tvCustomTextColor,
    UIColor* _tvCustomFocusedTextColor,
    long _textDragOptions,
    * _textDragDelegate,
    * _textDropDelegate,
    _UITextFieldVisualStyle* _visualStyle
)

// 类方法
<__NSArrayI 0x6000030b4930>(
    + (id)fallback_debugHierarchyValueForPropertyWithName:(id)arg0 onObject:(id)arg1 outOptions:(id *)arg2 outError:(id *)arg3 ,
    + (id)fallback_debugHierarchyPropertyDescriptions,
    + (BOOL)doc_setDefaultKeyboardAppearanceIfPossible:(long)arg0 ,
    + (BOOL)_isCompatibilityTextField
)

// 方法
(
    "- (void)setAb_textAttributes:(id)arg0 ",
    "- (void)setAb_text:(id)arg0 ",
    "- (id)ab_textAttributes",
    "- (id)ab_text",
    "- (void)_cnui_applyContactStyle",
    "- (void).cxx_destruct",
    "- (void)dealloc",
    "- (void)setAttributes:(id)arg0 range:({_NSRange=QQ})arg1 ",
    "- (void)setDelegate:(id)arg0 ",
    "- (void)setEnabled:(BOOL)arg0 ",
    "- (id)methodSignatureForSelector:(SEL)arg0 ",
    "- (id)forwardingTargetForSelector:(SEL)arg0 ",
    "- (id)font",
    "- (void)setNeedsLayout",
    "- (id)shadowColor",
    "- ({CGSize=dd})shadowOffset",
    "- (void)encodeWithCoder:(id)arg0 ",
    "- (id)initWithCoder:(id)arg0 ",
    "- (void)observeValueForKeyPath:(id)arg0 ofObject:(id)arg1 change:(id)arg2 context:(void *)arg3 ",
    "- (void)setProgress:(float)arg0 ",
    "- (id)delegate",
    "- (BOOL)respondsToSelector:(SEL)arg0 ",
    "- (id)textContainer",
    "- (id)layoutManager",
    "- (id)markedTextStyle",
    "- (id)typingAttributes",
    "- (void)setLabel:(id)arg0 ",
    ...共591个...
)

// 遵守的协议
<__NSFrozenArrayM 0x6000030d9ad0>(
    DebugHierarchyObject_Fallback ,
    ABText ,
    UIKeyboardInput ,
    _UILayoutBaselineUpdating ,
    _UIFloatingContentViewDelegate ,
    UIGestureRecognizerDelegate ,
    UIKeyInputPrivate ,
    _UITextFieldVisualStyleSubject ,
    UIViewGhostedRangeSupporting ,
    UITextInputTraits_Private ,
    _UITextFieldContent_Internal ,
    UIPopoverControllerDelegate ,
    _UITextFieldContentViewContextProvider,
    UITextDragSupporting ,
    UITextDropSupporting ,
    UITextPasteConfigurationSupporting_Internal ,
    UITextFieldContent <_UITextContent>,
    UITextDraggable ,
    UITextDroppable ,
    UITextPasteConfigurationSupporting ,
    UITextInput ,
    NSCoding,
    UIContentSizeCategoryAdjusting 
)


// 自己的协议描述
{
    "@optional" =     (
        "- (void)textFieldShouldReturn:",
        "- (void)textFieldShouldBeginEditing:",
        "- (void)textFieldDidBeginEditing:",
        "- (void)textFieldShouldEndEditing:",
        "- (void)textFieldDidEndEditing:",
        "- (void)textFieldDidEndEditing:reason:",
        "- (void)textField:shouldChangeCharactersInRange:replacementString:",
        "- (void)textFieldShouldClear:"
    );
}

// 层级关系
 -> UITextField -> UIControl -> UIView -> UIResponder -> NSObject



NSObject+DLIntrospection源码备份:
还没认真看,以后慢慢学习。

#import 

@interface NSObject (DLIntrospection)

/// 打印所有类
+ (NSArray *)classes;
/// 打印属性
+ (NSArray *)properties;
/// 内部变量
+ (NSArray *)instanceVariables;
/// 类方法
+ (NSArray *)classMethods;
/// 方法
+ (NSArray *)instanceMethods;

/// 遵守的协议
+ (NSArray *)protocols;
/// 自己的协议描述
+ (NSDictionary *)descriptionForProtocol:(Protocol *)proto;

/// 层级关系
+ (NSString *)parentClassHierarchy;

@end
#import "NSObject+DLIntrospection.h"
#import 

@interface NSString (DLIntrospection)

+ (NSString *)decodeType:(const char *)cString;

@end

@implementation NSString (DLIntrospection)

//https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
+ (NSString *)decodeType:(const char *)cString {
    if (!strcmp(cString, @encode(char))) return @"char";
    if (!strcmp(cString, @encode(int))) return @"int";
    if (!strcmp(cString, @encode(short))) return @"short";
    if (!strcmp(cString, @encode(long))) return @"long";
    if (!strcmp(cString, @encode(long long))) return @"long long";
    
    if (!strcmp(cString, @encode(unsigned char))) return @"unsigned char";
    if (!strcmp(cString, @encode(unsigned int))) return @"unsigned int";
    if (!strcmp(cString, @encode(unsigned short))) return @"unsigned short";
    if (!strcmp(cString, @encode(unsigned long))) return @"unsigned long";
    if (!strcmp(cString, @encode(unsigned long long))) return @"unsigned long long";
    
    if (!strcmp(cString, @encode(float))) return @"float";
    if (!strcmp(cString, @encode(double))) return @"double";
    
    if (!strcmp(cString, @encode(BOOL))) return @"BOOL";
    if (!strcmp(cString, @encode(void))) return @"void";
    if (!strcmp(cString, @encode(char *))) return @"char *";
    if (!strcmp(cString, @encode(id))) return @"id";
    if (!strcmp(cString, @encode(Class))) return @"class";
    if (!strcmp(cString, @encode(SEL))) return @"SEL";
    
//@TODO: do handle bitmasks
    NSString *result = [NSString stringWithCString:cString encoding:NSUTF8StringEncoding];
    if ([[result substringToIndex:1] isEqualToString:@"@"] && [result rangeOfString:@"?"].location == NSNotFound) {
        result = [[result substringWithRange:NSMakeRange(2, result.length - 3)] stringByAppendingString:@"*"];
    } else
    if ([[result substringToIndex:1] isEqualToString:@"^"]) {
        result = [NSString stringWithFormat:@"%@ *",
                   [NSString decodeType:[[result substringFromIndex:1] cStringUsingEncoding:NSUTF8StringEncoding]]];
    }
    return result;
}

@end

static void getSuper(Class class, NSMutableString *result) {
    [result appendFormat:@" -> %@", NSStringFromClass(class)];
    if ([class superclass]) { getSuper([class superclass], result); }
}


@implementation NSObject (DLIntrospection)

+ (NSArray *)classes {
    unsigned int classesCount;
    Class *classes = objc_copyClassList(&classesCount);
    NSMutableArray *result = [NSMutableArray array];
    for (unsigned int i = 0 ; i < classesCount; i++) {
        [result addObject:NSStringFromClass(classes[i])];
    }
    return [result sortedArrayUsingSelector:@selector(compare:)];
}

+ (NSArray *)classMethods {
    return [self methodsForClass:object_getClass([self class]) typeFormat:@"+"];
}

+ (NSArray *)instanceMethods {
    return [self methodsForClass:[self class] typeFormat:@"-"];
}

+ (NSArray *)properties {
    unsigned int outCount;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    NSMutableArray *result = [NSMutableArray array];
    for (unsigned int i = 0; i < outCount; i++) {
        [result addObject:[self formattedPropery:properties[i]]];
    }
    free(properties);
    return result.count ? [result copy] : nil;
}

+ (NSArray *)instanceVariables {
    unsigned int outCount;
    Ivar *ivars = class_copyIvarList([self class], &outCount);
    NSMutableArray *result = [NSMutableArray array];
    for (unsigned int i = 0; i < outCount; i++) {
        NSString *type = [NSString decodeType:ivar_getTypeEncoding(ivars[i])];
        NSString *name = [NSString stringWithCString:ivar_getName(ivars[i]) encoding:NSUTF8StringEncoding];
        NSString *ivarDescription = [NSString stringWithFormat:@"%@ %@", type, name];
        [result addObject:ivarDescription];
    }
    free(ivars);
    return result.count ? [result copy] : nil;
}

+ (NSArray *)protocols {
    unsigned int outCount;
    Protocol * const *protocols = class_copyProtocolList([self class], &outCount);

    NSMutableArray *result = [NSMutableArray array];
    for (unsigned int i = 0; i < outCount; i++) {
        unsigned int adoptedCount;
        Protocol * const *adotedProtocols = protocol_copyProtocolList(protocols[i], &adoptedCount);
        NSString *protocolName = [NSString stringWithCString:protocol_getName(protocols[i]) encoding:NSUTF8StringEncoding];

        NSMutableArray *adoptedProtocolNames = [NSMutableArray array];
        for (unsigned int idx = 0; idx < adoptedCount; idx++) {
            [adoptedProtocolNames addObject:[NSString stringWithCString:protocol_getName(adotedProtocols[idx]) encoding:NSUTF8StringEncoding]];
        }
        NSString *protocolDescription = protocolName;
        
        if (adoptedProtocolNames.count) {
            protocolDescription = [NSString stringWithFormat:@"%@ <%@>", protocolName, [adoptedProtocolNames componentsJoinedByString:@", "]];
        }
        [result addObject:protocolDescription];
        //free(adotedProtocols);
    }
    //free((__bridge void *)(*protocols));
    return result.count ? [result copy] : nil;
}

+ (NSDictionary *)descriptionForProtocol:(Protocol *)proto {
    NSMutableDictionary *methodsAndProperties = [NSMutableDictionary dictionary];
    
    NSArray *requiredMethods = [[[self class] formattedMethodsForProtocol:proto required:YES instance:NO] arrayByAddingObjectsFromArray:[[self class]formattedMethodsForProtocol:proto required:YES instance:YES]];
    
    NSArray *optionalMethods = [[[self class] formattedMethodsForProtocol:proto required:NO instance:NO] arrayByAddingObjectsFromArray:[[self class]formattedMethodsForProtocol:proto required:NO instance:YES]];

    unsigned int propertiesCount;
    NSMutableArray *propertyDescriptions = [NSMutableArray array];
    objc_property_t *properties = protocol_copyPropertyList(proto, &propertiesCount);
    for (unsigned int i = 0; i < propertiesCount; i++) {
        [propertyDescriptions addObject:[self formattedPropery:properties[i]]];
    }
    
    if (requiredMethods.count) {
        [methodsAndProperties setObject:requiredMethods forKey:@"@required"];
    }
    if (optionalMethods.count) {
        [methodsAndProperties setObject:optionalMethods forKey:@"@optional"];
    } if (propertyDescriptions.count) {
        [methodsAndProperties setObject:[propertyDescriptions copy] forKey:@"@properties"];
    }
    
    free(properties);
    return methodsAndProperties.count ? [methodsAndProperties copy ] : nil;
}

+ (NSString *)parentClassHierarchy {
    NSMutableString *result = [NSMutableString string];
    getSuper([self class], result);
    return result;
}

#pragma mark - Private

+ (NSArray *)methodsForClass:(Class)class typeFormat:(NSString *)type {
    unsigned int outCount;
    Method *methods = class_copyMethodList(class, &outCount);
    NSMutableArray *result = [NSMutableArray array];
    for (unsigned int i = 0; i < outCount; i++) {
        NSString *methodDescription = [NSString stringWithFormat:@"%@ (%@)%@",
                                       type,
                                       [NSString decodeType:method_copyReturnType(methods[i])],
                                       NSStringFromSelector(method_getName(methods[i]))];
        
        NSInteger args = method_getNumberOfArguments(methods[i]);
        NSMutableArray *selParts = [[methodDescription componentsSeparatedByString:@":"] mutableCopy];
        NSInteger offset = 2; //1-st arg is object (@), 2-nd is SEL (:)
        
        for (NSUInteger idx = offset; idx < args; idx++) {
            NSString *returnType = [NSString decodeType:method_copyArgumentType(methods[i], (unsigned int)idx)];
            selParts[idx - offset] = [NSString stringWithFormat:@"%@:(%@)arg%lu",
                                      selParts[idx - offset],
                                      returnType,
                                      idx - 2];
        }
        [result addObject:[selParts componentsJoinedByString:@" "]];
    }
    free(methods);
    return result.count ? [result copy] : nil;
}

+ (NSArray *)formattedMethodsForProtocol:(Protocol *)proto required:(BOOL)required instance:(BOOL)instance {
    unsigned int methodCount;
    struct objc_method_description *methods = protocol_copyMethodDescriptionList(proto, required, instance, &methodCount);
    NSMutableArray *methodsDescription = [NSMutableArray array];
    for (unsigned int i = 0; i < methodCount; i++) {
        [methodsDescription addObject:
         [NSString stringWithFormat:@"%@ (%@)%@",
          instance ? @"-" : @"+",
#warning return correct type
          @"void",
          NSStringFromSelector(methods[i].name)]];
    }
    
    free(methods);
    return  [methodsDescription copy];
}

+ (NSString *)formattedPropery:(objc_property_t)prop {
    unsigned int attrCount;
    objc_property_attribute_t *attrs = property_copyAttributeList(prop, &attrCount);
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    for (unsigned int idx = 0; idx < attrCount; idx++) {
        NSString *name = [NSString stringWithCString:attrs[idx].name encoding:NSUTF8StringEncoding];
        NSString *value = [NSString stringWithCString:attrs[idx].value encoding:NSUTF8StringEncoding];
        [attributes setObject:value forKey:name];
    }
    free(attrs);
    NSMutableString *property = [NSMutableString stringWithFormat:@"@property "];
    NSMutableArray *attrsArray = [NSMutableArray array];
    
    //https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5
    [attrsArray addObject:[attributes objectForKey:@"N"] ? @"nonatomic" : @"atomic"];
    
    if ([attributes objectForKey:@"&"]) {
        [attrsArray addObject:@"strong"];
    } else if ([attributes objectForKey:@"C"]) {
        [attrsArray addObject:@"copy"];
    } else if ([attributes objectForKey:@"W"]) {
        [attrsArray addObject:@"weak"];
    } else {
        [attrsArray addObject:@"assign"];
    }
    if ([attributes objectForKey:@"R"]) {[attrsArray addObject:@"readonly"];}
    if ([attributes objectForKey:@"G"]) {
        [attrsArray addObject:[NSString stringWithFormat:@"getter=%@", [attributes objectForKey:@"G"]]];
    }
    if ([attributes objectForKey:@"S"]) {
        [attrsArray addObject:[NSString stringWithFormat:@"setter=%@", [attributes objectForKey:@"G"]]];
    }
    
    [property appendFormat:@"(%@) %@ %@",
     [attrsArray componentsJoinedByString:@", "],
     [NSString decodeType:[[attributes objectForKey:@"T"] cStringUsingEncoding:NSUTF8StringEncoding]],
     [NSString stringWithCString:property_getName(prop) encoding:NSUTF8StringEncoding]];
    return [property copy];
}

@end

 

你可能感兴趣的:([iOS]学习笔记)