当前位置:正文

自由xxx管高清晰度午夜剧场免费会员1000 iOS一道复合型面试题与底层原理

admin | 2020-07-24 09:46 浏览数:

objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:), key, self, @{ NSKeyValueChangeNewKey: name}, nil);

下面我们根据KVO的实现细节,仿写一个 非常简化版 的KVO。

new array: (

person .firstName= @"Tom";

returnNO;

return@"Apple";

@end

程序员高薪盛宴背后: 程序员正在消失?

// 2.修改修改isa指针

returnkey;

[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@ "transactions"];

[ superviewDidLoad];

[self didChangeValueForKey:@ "itemChanged"];

@property( nonatomic, strong, nullable) NSString*firstName;

我们从热身开始,慢慢深入:

person1 .name= @"Jerry";

2. 由浅入深探索KVO

if(theBalance != _openingBalance) {

_openingBalance = theBalance;

self.p1 = [[Person alloc] init];

这个是Foundation框架中的一个私有方法 _NSSetObjectValueAndNotify 。

@interface ViewController

_openingBalance = theBalance;

- ( void)viewDidLoad {

// 使用 mutableArrayValueForKey: 检索关系代理对象

Address: Foundation[ 0x000000000006954b] (Foundation.__TEXT.__text 422667)

} else{

- ( void) setOpeningBalance:( double)theBalance {

)

keyPathsForValuesAffectingValueForKey: 方法不能支持一对多关系。

}

person2 .firstName= @"Jerry";

};

原标题:iOS一道复合型面试题与底层原理

returnNO;

// 1.创建一个子类

写 外挂赚 12万获刑,被抓才知道帮 团队赚 300万

2.2.2 IMP

NSString*selectorName = [ NSStringstringWithFormat: @"set%@:", keyPath .capitalizedString];

NSString*newName = [ NSStringstringWithFormat: @"CustomKVO_%@", oldName];

( NSSet*)keyPathsForValuesAffectingFullName {

@end

来源公众号丨知识小集(zsxjtip)

当一个对象的一个属性注册了观察者后,被观察对象的isa指针的就指向了一个系统为我们生成的中间类,而不是我们自己创建的类。在这个类中,系统为我们重写了被观察属性的setter方法。

@end

@implementation ViewController

person .lastName= @"Google";

NSLog( @"person full name: %@ %@", person .firstName, person .lastName);

new name: Tom

@property( nonatomic, strong, nullable) NSString*name;

[self willChangeValueForKey:@ "totalSalary"];

[ superviewDidLoad];

( BOOL)automaticallyNotifiesObserversForKey:( NSString*)key {

self.person.name= @"Tom";

@property( nonatomic, strong) Person *p1;

Person *person0 = [[Person alloc] init];

- ( void)viewDidLoad {

}

@property( nonatomic, strong, nullable) NSString*lastName;

@interface Person : NSObject

// [self.p2 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

NSLog( @"before kvo --- p2: %s", object_getClassName( self.p2));

我们来写一个例子:

现在要在创建了person实例后,修改lastName的返回值,将其固定返回 Apple 。

self.p1= [[Person alloc] init];

NSString*oldName = NSStringFromClass([ selfclass]);

@interface ViewController

NSString*getKeyForSetter( NSString*setter) {

}

[ superviewDidLoad];

NSLog( @"new name: %@", change[ NSKeyValueChangeNewKey]);

@property( nonatomic, strong, readonly) NSString*fullName;

person full name: Tom Apple

{

Person *person2 = [[Person alloc] init];

)

// 3.重写set方法

如果一个操作会导致多个属性改变,需要嵌套通知:

self.p2.name = @ "Jerry";

}

NSRangerange = NSMakeRange( 3, setter .length- 4);

NSLog( @"new person: %@", people .firstObject.name);

}

举个例子,比如你有一个Department对象,和很多个Employee对象。而Employee有一个salary属性。你可能希望Department对象有一个totalSalary的属性,依赖于所有的Employee的salary 。

重要

self.p2 = [[Person alloc] init];

2.1.1 自动通知 // 调用 set方法

但常见的考点我们是需要知道的。有时考点可能被包装了一下,可能没法一下就看出来,但看破考点之后就会有恍然大悟的感觉。因为本质还是一样的,都是新瓶装旧酒。就像原来的理科考试题,包装一个新的场景,让你解决这个场景下的一个问题自由xxx管高清晰度午夜剧场免费会员1000,但理论知识都是学过的。

@end

self.p1.name = @ "Tom";

}

@end

NSArray*affectingKeys = @[ @"lastName", @"firstName"];

key = [key stringByReplacingCharactersInRange: NSMakeRange( 0, 1) withString:letter];

@implementation ViewController

structobjc_super superClass = {

constchar*type = method_getTypeEncoding(method);

@end

// 2.重写get方法

}

person .firstName= @"Tom";

void_MySetObjectValueAndNotify( idself, SEL selector, NSString*name) {

通过 object_getClass(id obj) 方法可以获得实例对象真实的类(isa指针的指向)。

new name: Tom

必须注意不仅仅是这个key改变了自由xxx管高清晰度午夜剧场免费会员1000,还有它改变的类型以及索引。

一对多关系

// Remove the transaction objects at the specified indexes.

可以看到 _NSSetObjectValueAndNotify 还是调用了 willChangeValueForKey: 和 didChangeValueForKey: 来进行手动通知的。

self.p1.name = @ "Tom";

if([key isEqualToString: @"name"]) {

NSMutableArray *transactions = [account mutableArrayValueForKey:@ "transactions"];

// 输出

@property( nonatomic, strong) NSMutableArray<Person *> *people;

Transaction *newTransaction = < #Create a new transaction for the account#>;

@implementation Person

person .lastName= @"Google";

}

}

- ( void)observeValueForKeyPath:( NSString*)keyPath ofObject:( id)object change:( NSDictionary*)change context:( void*)context {

@implementation ViewController

- ( void)snx_addObserver:( NSObject*)observer forKeyPath:( NSString*)keyPath options:( NSKeyValueObservingOptions)options context:( void*)context {

// 输出

NSString*key = @"people";

// 输出

@property(nonatomic, strong, readonly) NSString *fullName;

// 输出

作者丨收納箱

class_addMethod(customClass, sel, (IMP)_MySetObjectValueAndNotify, "v@:@");

@interface Person : NSObject

[ self.peopleaddObject:person0];

objc_msgSendSuper(&superClass, selector, name);

person2 .lastName= @"Google";

- ( void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:( void*)context {

[ self.personsetValue: @"Jerry"forKey: @"name"];

NSString*letter = [[key substringToIndex: 1] lowercaseString];

[ self.personaddObserver: selfforKeyPath: @"name"options: NSKeyValueObservingOptionNewcontext: nil];

object_setClass( self, customClass);

keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];

2.3 自定义KVO

SEL sel = @selector(lastName);

我想通过这个面试题自由xxx管高清晰度午夜剧场免费会员1000,介绍一种如何在日常开发中使用 isa-swizzling 的思路。

[self willChangeValueForKey:@ "openingBalance"];

Too many arguments to function call, expected 0, have 3

可以重写 keyPathsForValuesAffectingValueForKey: 方法。也可以通过实现 keyPathsForValuesAffecting 方法来达到前面同样的效果自由xxx管高清晰度午夜剧场免费会员1000,这里的 就是属性名自由xxx管高清晰度午夜剧场免费会员1000,不过第一个字母要大写。

before kvo --- p2:Person

(

( NSSet*)keyPathsForValuesAffectingValueForKey:( NSString*)key {

@property( nonatomic, strong) Person *p2;

从结果中我们可以看到isa指针指向了一个中间类NSKVONotifying_Person。

}

[self willChangeValueForKey:@ "itemChanged"];

一对一关系

解决办法:在Build Setting修改Enable Strict Checking of objc_msgSend Calls为No。

}

"Person name: Tom",

NSLog( @"person full name: %@ %@", person .firstName, person .lastName);

}

一对多的关系

if(totalSalary != newTotalSalary) {

NSString*oldName = NSStringFromClass([person class]);

从输出结果可以看到自由xxx管高清晰度午夜剧场免费会员1000,我们使用 isa-swizzling 将person对象lastName的行为改变了自由xxx管高清晰度午夜剧场免费会员1000,而person2对象没有受到影响。

class_getSuperclass([ selfclass])

NSLog(@ "before kvo --- p2: %s", object_getClassName(self.p2));

person0 .name= @"Tom";

NSLog( @"new name: %@", change[ NSKeyValueChangeNewKey]);

- ( void)viewDidLoad {

重要

- ( void)observeValueForKeyPath:( NSString*)keyPath ofObject:( id)object change:( NSDictionary*)change context:( void*)context {

// deal with other observations and/or invoke super...

return[ NSStringstringWithFormat: @"%@ %@", self.firstName, self.lastName];

// 集合

[document setValue:@ "Savings"forKeyPath:@ "account.name"];

NSString*selectorName = NSStringFromSelector(selector);

class_addMethod(customClass, sel, (IMP)demo_getLastName, type);

NSLog(@ " after kvo --- p1: %p p2: %p", [self.p1 methodForSelector:@selector( setName:)], [self.p2 methodForSelector:@selector( setName:)]);

}

NSLog( @"person2 full name: %@ %@", person2 .firstName, person2 .lastName);

if([key isEqualToString: @"fullName"]) {

// 1.调用父类的方法

[self didChangeValueForKey:@ "openingBalance"];

@implementation Person

}

@implementation ViewController

2.1 KVO应用

[transactions addObject:newTransaction];

@end

@interfacePerson : NSObject

}

@interface Person : NSObject

// 输出

- ( void) setOpeningBalance:( double)theBalance {

person2 full name: Jerry Google

NSArray<Person *> *people = change[ NSKeyValueChangeNewKey];

@end

"<Person: 0x60000276cc20>"

NSObject*observer = objc_getAssociatedObject( self, &ObserverKey);

person2 .name= @"Frank";

@interface Person : NSObject

@property( nonatomic, strong, nullable) NSString*firstName;

2.1.3 键之间的依赖

[self willChangeValueForKey:@ "openingBalance"];

@property(nonatomic, strong, nullable) NSString *firstName;

return[ NSStringstringWithFormat: @"%@ %@", self.firstName, self.lastName];

_itemChanged = _itemChanged 1;

KVO的实现用了一种叫 isa-swizzling 的技术。

(lldb) image lookup -a 0x7fff 258e454b

NSLog(@ "before kvo --- p1: %p p2: %p", [self.p1 methodForSelector:@selector( setName:)], [self.p2 methodForSelector:@selector( setName:)]);

_totalSalary = newTotalSalary;

@property(nonatomic, strong, nullable) NSString *lastName;

// 或者

objc_setAssociatedObject( self, &ObserverKey, observer, OBJC_ASSO CIATION_ASSIGN);

self.person= [[Person alloc] init];

NSLog(@ "after kvo --- p2: %s", object_getClassName(self.p2));

// 实现一个setter和通知函数

People:

// 4.绑定观察者

0. 引言

[people addObject:person2];

self.p1 = [[Person alloc] init];

self.people= [ NSMutableArrayarray];

[self.p2 snx_addObserver:self forKeyPath:@ "name"options:NSKeyValueObservingOptionNew context:nil];

person full name: Tom Google

NSLog(@ "before kvo --- p1: %p p2: %p", [self.p1 methodForSelector:@selector( setName:)], [self.p2 methodForSelector:@selector( setName:)]);

- ( NSString*)fullName {

// 根据方法名获取Key

好了自由xxx管高清晰度午夜剧场免费会员1000,下面废话不多说自由xxx管高清晰度午夜剧场免费会员1000,进入我们的问题。

1. 面试题 1.1 题目

- ( void) setTotalSalary:(NSNumber *)newTotalSalary {

( BOOL)automaticallyNotifiesObserversOfName {

}

手动通知提供了更自由的方式去决定什么时间自由xxx管高清晰度午夜剧场免费会员1000,什么方式去通知观察者。想要使用手动通知必须实现 automaticallyNotifiesObserversForKey: (或者 automaticallyNotifiesObserversOf )方法。在一个类中同时使用自动和手动通知是可行的。对于想要手动通知的属性自由xxx管高清晰度午夜剧场免费会员1000,可以根据它的keyPath返回NO自由xxx管高清晰度午夜剧场免费会员1000,而其对于其他位置的keyPath自由xxx管高清晰度午夜剧场免费会员1000,要返回父类的这个方法。

@property( nonatomic, strong, nullable) NSString*lastName;

"Person name: Jerry",

2.1.2 手动通知

教你构建 Python 编程的核心知识体系!

@implementation Person

after kvo --- p2:CustomKVO_Person

[self.p2 addObserver:self forKeyPath:@ "name"options:NSKeyValueObservingOptionNew context:nil];

newname:Jerry

[self didChangeValueForKey:@ "totalSalary"];

@end

if(context == totalSalaryContext) {

} elseif([keyPath isEqualToString: @"people"]) {

@property( nonatomic, strong, nullable) NSString*lastName;

给大家再出一道简单的关于KVO日常应用的题。

使用objc_msgSendSuper时自由xxx管高清晰度午夜剧场免费会员1000,可能编译器会报错:

- ( void)viewDidLoad {

NSString*key = [setter substringWithRange:range];

}

- (NSNumber *)totalSalary {

[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@ "transactions"];

return_totalSalary;

return[ superautomaticallyNotifiesObserversForKey:key];

原文链接:

NSMutableArray*people = [ selfmutableArrayValueForKey:key];

[account setValue:@ "Savings"forKey:@ "name"];

// 1.创建一个子类

苹果的KVO中间类的命名规则是在类名前添加NSKVONotifying_自由xxx管高清晰度午夜剧场免费会员1000,如果我们的类叫SonKVO之后的中间类为NSKVONotifying_Son。

}

NSLog( @"People: n%@", self.people);

Person *person = [[Person alloc] init];

- ( NSString*)fullName {

分享史上Java最牛逼,最简短的代码

面试题1 现有一个继承于NSObject的实例对象,需要在不直接修改方法实现的情况下,改变一个方法的行为,你会怎么做? 不直接修改方法实现,指的是不直接修改.m文件中方法的内部实现 这一道题比较简单,其实问的就是 Runtime 的 Method Swizzling 。可能答出来之后,还会问几个 Method Swizzling 相关的深入问题。下面难度升级。 面试题2 问题1,如果使用 Method Swizzling 技术,相当于修改了类对象中方法选择器和IMP实现的对应关系。这将导致继承自这个类的所有子类和实例对象都影响,如何控制受影响的范围,或者说如何让方法的行为改变只对这个实例对象生效? 这个题难度上升了,但是不是有一种脱离生产的感觉,为了面试你而出的一道题? 我们对这个问题包装一下,让它看起来更接地气,同时问题也再升级一点。 面试题3 现有一个视图,我们需要扩大一下它的响应范围。如果使用 Method Swizzling 技术,受影响的范围会比较大。当然,也可以选择继承一个子类来实现。但如果现在实例已经创建了,还是同样的需求,你会如何实现? 现在问题开始接近生产了。一般来说,修改响应范围涉及到 响应链和事件传递 的知识点。 - hitTest:withEvent: - pointInside:withEvent: 现在限制了继承并创建子类实例 的方案,只能选择其他办法。 现在同样也限制了 Method Swizzling 方案,还有什么办法呢? 答案还是 Runtime 技术。但这个会涉及到2个 Runtime 考点:消息发送与转发 以及 isa-swizzling 。 消息发送与转发:主要是 objc_msgSend 之后的方法查找流程。如果继续深入问,会到 消息转发 相关的考点。 isa-swizzling :常见于 KVO 原理考点,但其实说到 isa-swizzling 肯定会伴随着 消息发送与转发 问题。因为修改了isa的指向,执行 objc_msgSend 时的查找流程会发生变化。 如果回答 Method Swizzling 技术,又涉及到影响范围问题,可能需要加开关、加扩大响应范围记录的变量等,则又涉及到 关联对象 相关的问题。 如果可以继承,当然可以选择复写两个方法来解决。 消息发送与转发:主要是 objc_msgSend 之后的方法查找流程。如果继续深入问,会到 消息转发 相关的考点。 isa-swizzling :常见于 KVO 原理考点,但其实说到 isa-swizzling 肯定会伴随着 消息发送与转发 问题。因为修改了isa的指向,执行 objc_msgSend 时的查找流程会发生变化。 如果回答 Method Swizzling 技术,又涉及到影响范围问题,可能需要加开关、加扩大响应范围记录的变量等,则又涉及到 关联对象 相关的问题。 如果可以继承,当然可以选择复写两个方法来解决。 展开全文

@end

Person *person1 = [[Person alloc] init];

Summary: Foundation`_NSSetObjectValueAndNotify

Method method = class_getInstanceMethod([person class], sel);

@end

下面是 KVO 原理,如果你非常自信已经熟悉这部分内容,可以不看了~

new name: Jerry

[ superviewDidLoad];

}

Person *person2 = [[Person alloc] init];

NSString*key = getKeyForSetter(selectorName);

@end

self.p2 = [[Person alloc] init];

}

@property( nonatomic, strong, nullable) NSString*firstName;

// 使用KVC forKey或 forKeyPath

在很多种情况下一个属性的值依赖于在其他对象中的属性。如果一个依赖属性的值改变了,这个属性也需要被通知到。

NSLog(@ "after kvo --- p1: %p p2: %p", [self.p1 methodForSelector:@selector( setName:)], [self.p2 methodForSelector:@selector( setName:)]);

NSSet*keyPaths = [ superkeyPathsForValuesAffectingValueForKey:key];

[self didChangeValueForKey:@ "openingBalance"];

}

其实,从第1问到第3问,问的核心都是 isa-swizzling ,但通过层层包装可能涉及到 多个知识点 ,变成一道复合型面试题。

注册Department成为所有Employee的观察者。当Employee被添加或者被移除时进行计算。

Person *person = [[Person alloc] init];

new person: Frank

"Person name: Frank"

@end

}

我们看到监听之间两个实例对象的 setName: 方法的函数地址相同,KVO之后p2实例对象的 setName: 方法地址变了。

before kvo --- p1:0x103514460p2:0x103514460

[ superviewDidLoad];

Too many arguments to function call, expected 0, have 3

object_setClass(person, customClass);

if([keyPath isEqualToString: @"name"]) {

}

@end

@implementation Person

after kvo --- p1:0x103514460p2:0x103513f90

示例

new name: Jerry

Class customClass = objc_allocateClassPair([ selfclass], newName .UTF8String, 0);

我们可以查看一下这个方法地址:

self.p2= [[Person alloc] init];

}

如何在修改firstName或lastName时,执行通知fullName变化了。如果你的思路是,在firstName或lastName的set方法中手动调用 willChangeValueForKey: 和 didChangeValueForKey: ,那么强烈建议阅读此部分。

[account setName:@ "Savings"];

NSLog( @"person full name: %@ %@", person .firstName, person .lastName);

}

@implementation Person

- ( void) removeTransactionsAtIndexes:(NSIndexSet *)indexes {

}

}

// 3.修改修改isa指针(isa swizzling)

return[ NSSetsetWithObjects: @"lastName", @"firstName", nil];

after kvo --- p1:0x10ccee670p2:0x7fff258e454b

@implementation Person

}

// 非集合

@property( nonatomic, strong) Person *person;

如果觉得这个面试题对你有所帮助,给我点个赞吧~