美文网首页
class_copylvarList&class_cop

class_copylvarList&class_cop

作者: 无题007 | 来源:发表于2017-07-19 11:25 被阅读279次

    写在开头

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

    怎么获取私有变量呢

    • 首先需要用class_copyIvarList这个方法获取到当前类的所有实例变量。
    +(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结构体,通过class_getInstanceVariable 这个方法就可以获得对应变量的Ivar信息。
    Ivar ivar = class_getInstanceVariable([BJ class]"要找的变量名");
    

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

    id xxx = object_getIvar(BJ,ivar);
    

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

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

    实战来袭

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

    • 首先,我们查找UITextView所有的实例变量
      [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));
    

    相关文章

      网友评论

          本文标题:class_copylvarList&class_cop

          本文链接:https://www.haomeiwen.com/subject/qgdhkxtx.html