class_copylvarList&class_cop

2017-07-19  本文已影响279人  无题007

写在开头

通过前面的介绍,我们可以知道对象的实例变量存在于Class结构体的一个ivars的链表中,同时runtime提供了丰富的函数对其进行操作。但是我觉得,还是私有变量更能引起我的兴趣。

怎么获取私有变量呢

+(void)getAllIvarNameWithClass:(Class)YSClass Completed:(void(^)(NSArray *ivarNameArray))completed{
    NSMutableArray *ivarNameArray = [NSMutableArray array];
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(YSClass,&count);
    for(int i = 0;i<count;i++){
      Ivar ivar = ivars[i];
      const char *ivarNameCode = [NSString stringWithUTF8String:ivarName];
      [ivarNameArray addObject:ivarNameCode];
}
  if(completed) completed(ivarNameArray);
}
Ivar ivar = class_getInstanceVariable([BJ class]"要找的变量名");

下面就要通过object_getIvar()这个方法拿到它了

id xxx = object_getIvar(BJ,ivar);

还可以改变里面的一个变量的值 用

[BJ setValue:aaa forKey:@"bbb"];//用 aaa替换bbb

实战来袭

这里我们以给UITextView添加PlaceHolder为例

  [NSObject getAllIvarNameWithClass:[UITextView class] Completed:^(NSArray *ivarNameArray){
    NSLog(@"ivars_%@",ivarNameArray);
} ];

打印结果:

"_private",
"_textStorage",
... ...
"_preferredMaxLayoutWidth",
"_placeholderLabel",
"_inputAccessoryView",
... ...
"_inputView"

我们会发现里面有一个叫做_placeholderLabel的实例变量,当我们用上面的方式去找这个对象的时候,会发现我们得到的是nil,那么可能是它没有被初始化,所以这里我们用kvc的方式。

//给TextView添加PlaceHolder
UITextView *textView = [[UITextView alloc]initWithFrame:CGRectMake(10, 50, CGRectGetWidth(self.view.frame) - 20, 200)];
[textView setBackgroundColor:[UIColor whiteColor]];
textView.font = [UIFont systemFontOfSize:16];
textView.delegate = self;
[self.view addSubview:textView];
UILabel *placeHolderLabel = [[UILabel alloc] init];
placeHolderLabel.text = @"我是PlaceHolder。";
placeHolderLabel.numberOfLines = 0;
placeHolderLabel.textColor = [UIColor lightGrayColor];
[placeHolderLabel sizeToFit];
placeHolderLabel.font = textView.font;
[textView addSubview:placeHolderLabel];
[textView setValue:placeHolderLabel forKey:@"_placeholderLabel"];

然后再去获取PlaceHolder:

Ivar placeHolderIvar = class_getInstanceVariable([UITextView class],"_placeholderLabel");
id getPH = object_getIvar(textView,placeHolderIvar);
NSLog(@"placeHolder_%p_%p",placeHolderLabel,getPH);

class_copyIvarList的用法还有很多,这里只是稍微介绍一下

class_copyPropertyList

获得所有的属性:

/**
 *获取当前类的所有属性
 */
+(void)getAllPropertyNameWithClass:(Class)YSClass Completed:(void (^)(NSArray *propertyNameArray))completed{
    NSMutableArray *propertyNameArray = [NSMutableArray array];
    unsigned int propertyCount = 0;
    objc_property_t *propertys = class_copyPropertyList(YSClass, &propertyCount);
    for (int i = 0; i < propertyCount; i++){
        objc_property_t property = propertys[i];
        const char *propertysName = property_getName(property);
        NSString *propertysNameCode = [NSString stringWithUTF8String:propertysName];
        [propertyNameArray addObject:propertysNameCode];
    }
    if (completed) completed(propertyNameArray);
}

我们同时调用获取实例变量以及属性的方法做一下对比:

[NSObject getAllIvarNameWithClass:[UITextView class] Completed:^(NSArray *ivarNameArray) {
    NSLog(@"ivars_%@",ivarNameArray);
}];
[NSObject getAllPropertyNameWithClass:[UITextView class] Completed:^(NSArray *propertyNameArray) {
    NSLog(@"propertys_%@",propertyNameArray);
}];

打印结果:

//实例变量
ivars_(
    "_private",
    "_textStorage",
    "_textContainer",
    "_layoutManager",
    "_containerView",
    "_inputDelegate",
    "_tokenizer",
    "_inputController",
    "_interactionAssistant",
    "_textInputTraits",
    "_autoscroll",
    "_tvFlags",
    "_contentSizeUpdateSeqNo",
    "_scrollTarget",
    "_scrollPositionDontRecordCount",
    "_scrollPosition",
    "_offsetFromScrollPosition",
    "_linkInteractionItem",
    "_dataDetectorTypes",
    "_preferredMaxLayoutWidth",
    "_placeholderLabel",
    "_inputAccessoryView",
    "_linkTextAttributes",
    "_streamingManager",
    "_characterStreamingManager",
    "_siriAnimationStyle",
    "_siriParameters",
    "_firstBaselineOffsetFromTop",
    "_lastBaselineOffsetFromBottom",
    "_cuiCatalog",
    "_beforeFreezingTextContainerInset",
    "_duringFreezingTextContainerInset",
    "_beforeFreezingFrameSize",
    "_unfreezingTextContainerSize",
    "_adjustsFontForContentSizeCategory",
    "_clearsOnInsertion",
    "_multilineContextWidth",
    "_inputView"
)
//属性
propertys_(
    "_drawsDebugBaselines",
    hash,
    superclass,
    description,
    debugDescription,
    delegate,
    text,
    font,
    textColor,
    textAlignment,
    selectedRange,
    editable,
    selectable,
    dataDetectorTypes,
    allowsEditingTextAttributes,
    attributedText,
    typingAttributes,
    inputView,
    inputAccessoryView,
    clearsOnInsertion,
    textContainer,
    textContainerInset,
    layoutManager,
    textStorage,
    linkTextAttributes,
    hash,
    superclass,
    description,
    debugDescription,
    autocapitalizationType,
    autocorrectionType,
    spellCheckingType,
    keyboardType,
    keyboardAppearance,
    returnKeyType,
    enablesReturnKeyAutomatically,
    secureTextEntry,
    textContentType,
    recentInputIdentifier,
    validTextRange,
    PINEntrySeparatorIndexes,
    textTrimmingSet,
    insertionPointColor,
    selectionBarColor,
    selectionHighlightColor,
    selectionDragDotImage,
    insertionPointWidth,
    textLoupeVisibility,
    textSelectionBehavior,
    textSuggestionDelegate,
    isSingleLineDocument,
    contentsIsSingleValue,
    hasDefaultContents,
    acceptsEmoji,
    acceptsDictationSearchResults,
    forceEnableDictation,
    forceDisableDictation,
    forceDefaultDictationInfo,
    forceDictationKeyboardType,
    emptyContentReturnKeyType,
    returnKeyGoesToNextResponder,
    acceptsFloatingKeyboard,
    acceptsSplitKeyboard,
    displaySecureTextUsingPlainText,
    displaySecureEditsUsingPlainText,
    learnsCorrections,
    shortcutConversionType,
    suppressReturnKeyStyling,
    useInterfaceLanguageForLocalization,
    deferBecomingResponder,
    enablesReturnKeyOnNonWhiteSpaceContent,
    autocorrectionContext,
    responseContext,
    inputContextHistory,
    disablePrediction,
    disableInputBars,
    isCarPlayIdiom,
    textScriptType,
    devicePasscodeEntry,
    hasText,
    selectedTextRange,
    markedTextRange,
    markedTextStyle,
    beginningOfDocument,
    endOfDocument,
    inputDelegate,
    tokenizer,
    textInputView,
    selectionAffinity,
    insertDictationResultPlaceholder,
    adjustsFontForContentSizeCategory
)

仔细看一下上面的输出,会发现,并不是每一个属性都对应一个自己的实例变量,那这是为啥呢?下面大家看一个🌰:
我们创建一个Person类:

.h
@interface Person : NSObject{
    NSInteger age;
}
@property(nonatomic,strong)NSString *name;
@end
-------------------------------------------------------------
.m
@implementation Person
-(void)setName:(NSString *)name{
    _name = name;
}

-(NSString *)name{
    return _name;
}
@end

这里会发现报错了,没有发现这个实例变量

我们再创建一个Person的分类:

.h
@interface Person (Character)
@property(nonatomic,strong)NSString* name;
@end
-------------------------------------------------------------
.m
@implementation Person (Character)
@end

我们打印这个类的实例变量和属性:

[NSObject getAllIvarNameWithClass:[Person class] Completed:^(NSArray *ivarNameArray) {
    NSLog(@"ivars_%@",ivarNameArray);
}];
[NSObject getAllPropertyNameWithClass:[Person class] Completed:^(NSArray *propertyNameArray) {
    NSLog(@"propertys_%@",propertyNameArray);
}];

打印结果:

ivars_(
    age
)
propertys_(
    name
)

一般情况下,声明一个属性相当于Ivar+setter方法+getter方法(但是Ivar+setter方法+getter方法并不代表它就是属性),也就是相当于在Class结构体中Ivars链表中添加一个Ivar,同时在methodLists添加两个Method。但是,在特定情况下,属性不会生成对应的实例变量,包括settergetter方法也有特定的生成规则。 那么这个东西有什么用呢?在前面我们说过objc_msgSend这个方法可以无限制的调用公开哪怕私有方法,并且我们对此进行了封装,那我们就可以两者结合,通过settergetter方法给属性赋值以及获取属性。

UITextView *textView = [[UITextView alloc]initWithFrame:CGRectMake(10, 50, CGRectGetWidth(self.view.frame) - 20, 200)];
[textView setBackgroundColor:[UIColor whiteColor]];
textView.font = [UIFont systemFontOfSize:16];
textView.delegate = self;
[self.view addSubview:textView];
((void (*) (id , SEL, id)) (void *)objc_msgSend) (textView, sel_registerName("setText:"), @"我是acceptsEmoji");
bool acceptsEmoji = ((bool (*) (id, SEL)) (void *)objc_msgSend)(textView, sel_registerName("acceptsEmoji"));
NSLog(@"acceptsEmoji_%@",@(acceptsEmoji));
上一篇下一篇

猜你喜欢

热点阅读