具有 2 列 y=mx 的表格视图示例

发布于 2024-10-07 19:55:59 字数 918 浏览 0 评论 0原文

我如何为 y=mx 的函数制作一个具有 2 列(x 和 y)的表格视图

我尝试了很多东西,所有这些都以完全失败告终。

有人可以给我一个示例代码并解释一下吗?

我问了又问,人们指导我各种 bool 教程,以及如何复制和粘贴内容,如何保存到文件,如何创建打开的应用程序列表,这些都没有帮助我,因为它们太过了复杂,

我有这个

 //array.m

    #import "array.h"

    @implementation array

    - (IBAction)makeArrays:(id)sender 
{
    int x,y;
    NSNumber *multiplier=[NSNumber numberWithFloat:[mField floatValue]];

    for (x=0;x++;x<181)
    {
        y=[multiplier floatValue]*x;

        NSNumber *xValue = [NSNumber numberWithInt:x];
        NSNumber *yValue = [NSNumber numberWithInt:x];

    NSArray  *xArray = [NSArray arrayWithObject:xValue];
    NSArray  *yArray = [NSArray arrayWithObject:yValue];
    }
}

@end

和类文件,

//array.h

#import <Cocoa/Cocoa.h>

@interface array : NSObject {
IBOutlet id mField; 
}
- (IBAction)makeArrays:(id)sender;

@end

我该从这里去哪里?

How would I make a tableview with 2 columns (x and y) for a function of y=mx

Ive tried lots of things, all of which end in complete failure.

Can someone please make me a sample code and explain it.

Ive asked and asked and people have directed me to all sorts of tutorials of bool, and how to copy and paste contents, how to save to a file, how to make a list of open applications, none of which help me because they are overly complicated

i have this

 //array.m

    #import "array.h"

    @implementation array

    - (IBAction)makeArrays:(id)sender 
{
    int x,y;
    NSNumber *multiplier=[NSNumber numberWithFloat:[mField floatValue]];

    for (x=0;x++;x<181)
    {
        y=[multiplier floatValue]*x;

        NSNumber *xValue = [NSNumber numberWithInt:x];
        NSNumber *yValue = [NSNumber numberWithInt:x];

    NSArray  *xArray = [NSArray arrayWithObject:xValue];
    NSArray  *yArray = [NSArray arrayWithObject:yValue];
    }
}

@end

and class file

//array.h

#import <Cocoa/Cocoa.h>

@interface array : NSObject {
IBOutlet id mField; 
}
- (IBAction)makeArrays:(id)sender;

@end

where do i go from here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

最终幸福 2024-10-14 19:55:59

在 OOP 中你应该做的第一件事是考虑对象的类。 Cocoa 使用 MVC(模型、视图、控制器)架构,因此类应该属于这三个类别之一。 Cocoa 已经提供了 NSTableView 类,它运行得很好,所以剩下的就是模型和控制器了。

对于模型类,您可以采用多种不同的方法:

  • 您可以编写一个函数表类,将 x 和 y 值保存在单独的数组中
  • 您可以编写一个包含单个 (x,y) 对数组的函数表类。
    • 在这个或之前的实现中,您可以提供一个支持这两种安排的公共接口(即,它们具有返回给定 x 的方法,以及 x、y 和 (x,y) 集合的属性)。一些实现细节取决于您如何将表视图连接到数据(绑定或旧的 NSTableViewDataSource 协议)。
  • 您还可以使用 x 值数组,并创建 价值转换器。使用这种方法,y 值存在于表视图中,而不是模型中。
  • 依此类推,

应用程序的需求将决定采取哪种方法。我将向您展示值转换器方法,因为它需要最少的代码。

对于控制器,您可以依赖 NSArrayController (它与 NSTableView 配合得很好),或者创建您自己的控制器。例如,您可以使用 NSMutableArray 作为模型,并创建一个将数组中的值映射到其他值的控制器。该控制器可以使用您定义的块或某些功能类来执行映射。

如您所见,有很多选择。我将选择需要最少编码的选项:一个值转换器、一个用于控制器的 NSArrayController 和一个用于模型的 NSMutableArray(存储在还存储值转换器的对象中)。在下文中,代码应按照标准约定存储在文件中:每个接口和实现都位于一个单独的文件中,其名称与类相同,接口扩展名为“.h”,实现扩展名为“.m”。我也不会理会常见的导入语句,例如 Cocoa/Cocoa.h 和每个类实现自己的接口。

首先,价值转换器。实际上,有两个,一个抽象超类和一个具体子类。这种分离是为了让您以后可以轻松添加其他函数类型。超类 FunctionTransformer 非常简单。所有需要从其基础 NSValueTransformer 重写的是返回转换值类的方法,transformedValueClass

@interface FunctionTransformer : NSValueTransformer 
+ (Class)transformedValueClass;
@end

@implementation Function
+ (Class)transformedValueClass {
    return [NSNumber class];
}
@end

具体子类 LinearTransformer 需要重写值转换器的主要方法:transformedValue:。由于线性变换是可逆的,我们还将提供 reverseTransformedValue:。它还需要斜率和截距值的属性。

#import "FunctionTransformer.h"

@interface LinearTransformer : FunctionTransformer {
    NSNumber *m_;
    NSNumber *b_;
}
@property (nonatomic,retain) NSNumber *slope;
@property (nonatomic,retain) NSNumber *intercept;


+ (BOOL)allowsReverseTransformation;

-(id)init;
-(id)initWithSlope:(float)slope;
-(id)initWithIntercept:(float)intercept;
-(id)initWithSlope:(float)slope intercept:(float)intercept;

-(void)dealloc;

-(NSNumber*)transformedValue:(id)value;
-(NSNumber*)reverseTransformedValue:(id)value;
@end


@implementation LinearTransformer
@synthesize slope=m_, intercept=b_;

+(BOOL)allowsReverseTransformation {
    return YES;
}
-(id)initWithSlope:(float)m intercept:(float)b {
    if ((self = [super init])) {
        m_ = [[NSNumber alloc] initWithFloat:m];
        b_ = [[NSNumber alloc] initWithFloat:b];
    }
    return self;    
}

-(id)init {
    return [self initWithSlope:1.0 intercept:0.0];
}
-(id)initWithSlope:(float)slope {
    return [self initWithSlope:slope intercept:0.0];
}
-(id)initWithIntercept:(float)intercept {
    return [self initWithSlope:1.0 intercept:intercept];    
}


-(void)dealloc {
    [b release];
    [m release];
    [super dealloc];
}


-(NSNumber*)transformedValue:(id)value {
    return [NSNumber numberWithFloat:([value floatValue] * [m floatValue] + [b floatValue])];
}

-(NSNumber*)reverseTransformedValue:(id)value {
    return [NSNumber numberWithFloat:(([value floatValue] - [b floatValue]) / [m floatValue])];
}
@end

需要注册特定的LinearTransformer才能使用,以便您可以设置斜率和截距。应用程序委托可以拥有此转换器(以及 x 值集合),或者您可以编写自定义控制器。我们将编写一个模型类,将 x 值和值转换器捆绑在一起,命名为 FunctionTable。设置函数转换器需要一个子任务:将转换器注册为值转换器(使用 +setValueTransformer:forName:)。这意味着我们需要为函数转换器属性 (f) 提供我们自己的 setter (setF:)。

#import "FunctionTransformer.h"

extern NSString* const kFunctionTransformer;

@interface FunctionTable : NSObject {
    NSMutableArray *xs;
    FunctionTransformer *f;
}
@property (nonatomic,retain) IBOutlet NSMutableArray *xs;
@property (nonatomic,retain) IBOutlet FunctionTransformer *f;
@end

// FunctionTable.m:
#import "LinearTransformer.h"

NSString* const kFunctionTransformer = @"Function Transformer";

@implementation FunctionTable
@synthesize xs, f;

-(id) init {
    if ((self = [super init])) {
        xs = [[NSMutableArray alloc] init];
        self.f = [[LinearTransformer alloc] init];
        [f release];
    }
    return self;
}
-(void)dealloc {
    [f release];
    [xs release];
    [super dealloc];
}

-(void)setF:(FunctionTransformer *)func {
    if (func != f) {
        [f release];
        f = [func retain];
        [NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
    }
}
@end

默认情况下,FunctionTable 使用 LinearTransformer。如果您想使用其他表,只需设置 FunctionTablesf 属性即可。您可以在 Interface Builder (IB) 中执行此操作通过使用绑定。请注意,在这个简单的实现中,值转换器始终以“Function Transformer”名称注册,从而有效地将您限制为一个 FunctionTable。更复杂的方案是为每个FunctionTable提供自己的函数转换器名称,该名称将在注册自己的FunctionTransformer时使用。

要设置所有内容:

  1. 在 IB 中打开应用程序的主窗口笔尖。
  2. 实例化 NSArrayController 和 FunctionTable(以及您的自定义应用程序委托,如果有)。
  3. 在主窗口中添加:
    1. 用于添加和删除元素的按钮,
    2. 用于斜率和截距的标签和 NSTextFields,
    3. 一个 NSTableView。
  4. 将表标题设置为“x”和“y”(应用程序运行不需要)
  5. 设置 连接
    • 有添加&删除发送到 NSArrayController 的 add:remove: 操作的按钮。
    • 将 NSTextFields 值绑定到 FunctionTables 的 f.slopef.intercept 键路径。
    • 将 NSTableView 两列的值绑定到 FunctionTables 的 xs
    • 将第二列的值转换器设置为“函数转换器”
    • 将 NSArrayController 的内容数组绑定到 FunctionTable 的 xs 键。
    • 如果您有应用委托,请将其连接到文件所有者的delegate 插座。

现在构建并运行。您可以使用添加和删除按钮向表中添加行或从表中删除行。您可以连续编辑“x”和“y”列(后者要感谢 reverseTransformedValue:)。您可以按“x”或“y”列排序。您可以更改斜率和截距,但除非您单独选择行,否则您不会注意到表中的更新。

高级主题

要解决表视图更新问题,我们需要将一个对象(FunctionTransformer)属性的更改传播到另一个对象(FunctionTable)属性的更改。我们将拥有 FunctionTable 观察其函数转换器属性的变化,并且当FunctionTable收到任何此类属性已更改的通知时,发送xs属性已更改的通知(这有点滥用,因为 xs 实际上并没有改变)。这会变得有点神奇,所以请耐心等待。

一个对象使用 KVO< 订阅另一个对象的更改/a> 方法 addObserver:forKeyPath:options:context: 另一个对象,并使用 removeObserver:forKeyPath:。这些方法只需要调用,不需要编写。通知由 observeValueForKeyPath:ofObject:change:context: 观察对象的方法,所以需要写这个方法。最后,对象可以通过调用 willChangeValueForKey:didChangeValueForKey:。还有其他方法可以发送仅集合的一部分已更改的通知,但我们在这里不会使用它们。

我们的 FunctionTable 可以处理更改订阅和取消订阅,但它必须知道要观察函数转换器的哪些属性,这意味着您无法更改转换器的类型。您可以向每个具体函数转换器添加方法来订阅和取消订阅观察者:

@implementation LinearTransformer
...
-(void)addObserver:(NSObject *)observer 
                   options:(NSKeyValueObservingOptions)options 
                   context:(void *)context 
{
    [self addObserver:observer
           forKeyPath:@"slope"
              options:options
              context:context];
    [self addObserver:observer
           forKeyPath:@"intercept"
              options:options
              context:context];
}
-(void)removeObserver:(id)observer {
    [self removeObserver:observer forKeyPath:@"slope"];
    [self removeObserver:observer forKeyPath:@"intercept"];
}
@end

但是,这将需要在每个方法中以及每个具体函数转换器中重复相当多的代码。使用一些魔法(反射闭包,或者在 Objective-C 中称为 ([2] )),我们可以添加方法(名为 addObserver:options:context:removeObserver:,因为它们在功能上类似于用于订阅的 KVO 方法; 取消订阅)FunctionTransformer,甚至NSObject。由于观察对象上的所有属性不仅限于 FunctionTransformer,因此我们将向 NSObject 添加方法。为此,您需要 OS X 10.6 或 PLBlocks 和 OS X 10.5。

让我们从上到下开始,对 FunctionTable 进行更改。设置函数转换器时现在有新的子任务:取消订阅对旧转换器的更改并订阅对新转换器的更改。因此,需要更新 setF: 方法以使用 NSObject 的新方法,这些方法将在名为“NSObject_Properties.h”的标头中定义。请注意,我们还不需要担心这些方法的实现。我们可以在这里使用它们,相信我们稍后会编写合适的实现。 FunctionTable 还需要一个新方法来处理更改通知(前面提到的 observeValueForKeyPath:ofObject:change:context:)。

#import "NSObject_Properties.h"
@interface FunctionTable
...
-(void)setF:(FunctionTransformer *)func {
    if (func != f) {
        [f removeObserver:self];
        [f release];
        f = [func retain];
        [f addObserver:self
               options:NSKeyValueObservingOptionPrior
               context:NULL];
        [NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
    }
}


- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (object == f) {
        if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
            [self willChangeValueForKey:@"xs"];
        } else {
            [self didChangeValueForKey:@"xs"];
        }
    }
}

接下来,我们在 NSObject 上编写新方法。订阅或取消订阅更改的方法将循环访问对象的属性,因此我们需要一个辅助方法 forEachProperty 来执行循环。此辅助方法将采用一个对每个属性调用的块。订阅和取消订阅方法将简单地调用 forEachProperty,传递一个调用标准 KVO 方法的块(addObserver:forKeyPath:options:context:removeObserver:forKeyPath:)在每个属性上添加或删除订阅。

//NSObject_Properties.h
#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>

@interface NSObject (Properties)
typedef void (^PropertyBlock)(objc_property_t prop, NSString *name);
-(void)forEachProperty:(PropertyBlock)block;

-(void)addObserver:(id)observer options:(NSKeyValueObservingOptions)options context:(void *)context;
-(void)removeObserver:(id)observer;
@end

// NSObject_Properties.m:
...
@implementation NSObject (Properties)
-(void)forEachProperty:(PropertyBlock)block {
    unsigned int propCount, i;
    objc_property_t * props = class_copyPropertyList([self class], &propCount);
    NSString *name;

    for (i=0; i < propCount; ++i) {
        name = [[NSString alloc] 
                    initWithCString:property_getName(props[i]) 
                    encoding:NSUTF8StringEncoding];
        block(props[i], name);
        [name release];
    }

    free(props);
}

-(void)addObserver:(NSObject *)observer 
           options:(NSKeyValueObservingOptions)options 
           context:(void *)context 
{
    [self forEachProperty:^(objc_property_t prop, NSString *name) {
        [self addObserver:observer
               forKeyPath:name
                  options:options
                  context:context];

    }];
}
-(void)removeObserver:(id)observer {
    [self forEachProperty:^(objc_property_t prop, NSString *name) {
        [self removeObserver:observer forKeyPath:name];
    }]; 
}
@end

The first thing you should do in OOP is consider the classes of objects. Cocoa uses an MVC (Model, View, Controller) architecture, so classes should fit in one of these three categories. Cocoa already provides the NSTableView class which works quite well, so that leaves the model and controller.

There are a number of different approaches to the model class you could take:

  • You could write a function table class that holds x and y values in separate arrays
  • You could write a function table class that has a single array of (x,y) pairs.
    • In either this or the previous implementation, you could provide a public interface that supports both arrangements (i.e. they'd have methods that return a y given an x, and properties that are x, y, and (x,y) collections). Some implementation details would depend on how you're connecting the table view to the data (bindings, or the older NSTableViewDataSource protocol).
  • You could also use an array of x values, and create a value transformer. With this approach, the y-values exist in the table view and not the model.
  • And so on

The application requirements will determine which approach to take. I'll show you the value transformer approach, as it requires the least amount of code.

For the controller, you could rely on NSArrayController (which works quite well with NSTableView), or create your own. For example, you could use an NSMutableArray as the model, and create a controller that maps the values from the array to other values. This controller could perform the mapping using blocks or some function classes that you define.

As you see, there are quite a few options. I'm going to go with the option that requires the least coding: a value transformer, an NSArrayController for the controller and an NSMutableArray (stored in an object that also stores a value transformer) for the model. In the following, code should be stored in files following the standard convention: each interface and implementation is in a separate file with name equal to the class, and an extension of ".h" for interfaces and ".m" for implementation. I also won't bother with the common import statements, such as for Cocoa/Cocoa.h and each class implementation's own interface.

First, the value transformer. Actually, there are two, an abstract superclass and a concrete subclass. This separation is so that you can easily add other function types later. The superclass, FunctionTransformer, is very simple. All that needs to be overridden from its base, NSValueTransformer, is the method that returns the class of transformed values, transformedValueClass:

@interface FunctionTransformer : NSValueTransformer 
+ (Class)transformedValueClass;
@end

@implementation Function
+ (Class)transformedValueClass {
    return [NSNumber class];
}
@end

The concrete subclass, LinearTransformer, needs to override the primary method of value transformers: transformedValue:. Since linear transforms are invertible, we'll also provide a reverseTransformedValue:. It will also need properties for the slope and intercept values.

#import "FunctionTransformer.h"

@interface LinearTransformer : FunctionTransformer {
    NSNumber *m_;
    NSNumber *b_;
}
@property (nonatomic,retain) NSNumber *slope;
@property (nonatomic,retain) NSNumber *intercept;


+ (BOOL)allowsReverseTransformation;

-(id)init;
-(id)initWithSlope:(float)slope;
-(id)initWithIntercept:(float)intercept;
-(id)initWithSlope:(float)slope intercept:(float)intercept;

-(void)dealloc;

-(NSNumber*)transformedValue:(id)value;
-(NSNumber*)reverseTransformedValue:(id)value;
@end


@implementation LinearTransformer
@synthesize slope=m_, intercept=b_;

+(BOOL)allowsReverseTransformation {
    return YES;
}
-(id)initWithSlope:(float)m intercept:(float)b {
    if ((self = [super init])) {
        m_ = [[NSNumber alloc] initWithFloat:m];
        b_ = [[NSNumber alloc] initWithFloat:b];
    }
    return self;    
}

-(id)init {
    return [self initWithSlope:1.0 intercept:0.0];
}
-(id)initWithSlope:(float)slope {
    return [self initWithSlope:slope intercept:0.0];
}
-(id)initWithIntercept:(float)intercept {
    return [self initWithSlope:1.0 intercept:intercept];    
}


-(void)dealloc {
    [b release];
    [m release];
    [super dealloc];
}


-(NSNumber*)transformedValue:(id)value {
    return [NSNumber numberWithFloat:([value floatValue] * [m floatValue] + [b floatValue])];
}

-(NSNumber*)reverseTransformedValue:(id)value {
    return [NSNumber numberWithFloat:(([value floatValue] - [b floatValue]) / [m floatValue])];
}
@end

A specific LinearTransformer needs to be registered to be used so that you can set the slope and intercept. The application delegate could own this transformer (along with the x value collection), or you could write a custom controller. We're going to write a model class that bundles together the x values and the value transformer, named FunctionTable. Setting the function transformer requires a sub tasks: registering the transformer as a value transformer (using +setValueTransformer:forName:). This means we'll need to provide our own setter (setF:) for the function transformer property (f).

#import "FunctionTransformer.h"

extern NSString* const kFunctionTransformer;

@interface FunctionTable : NSObject {
    NSMutableArray *xs;
    FunctionTransformer *f;
}
@property (nonatomic,retain) IBOutlet NSMutableArray *xs;
@property (nonatomic,retain) IBOutlet FunctionTransformer *f;
@end

// FunctionTable.m:
#import "LinearTransformer.h"

NSString* const kFunctionTransformer = @"Function Transformer";

@implementation FunctionTable
@synthesize xs, f;

-(id) init {
    if ((self = [super init])) {
        xs = [[NSMutableArray alloc] init];
        self.f = [[LinearTransformer alloc] init];
        [f release];
    }
    return self;
}
-(void)dealloc {
    [f release];
    [xs release];
    [super dealloc];
}

-(void)setF:(FunctionTransformer *)func {
    if (func != f) {
        [f release];
        f = [func retain];
        [NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
    }
}
@end

By default, FunctionTable uses a LinearTransformer. If you want to use a different one, simply set the FunctionTables's f property. You could do this in Interface Builder (IB) by using bindings. Note that in this simplistic implementation, the value transformer is always registered under the name "Function Transformer", effectively limiting you to one FunctionTable. A more complex scheme would be to give every FunctionTable their own function transformer name which would be used when registering their own FunctionTransformer.

To set everything up:

  1. Open the app's main window nib in IB.
  2. Instantiate an NSArrayController and a FunctionTable (and your custom app delegate, if any).
  3. To the main window, add:
    1. Buttons to add and remove elements,
    2. labels and NSTextFields for the slope and intercept,
    3. an NSTableView.
  4. Set the table headers to "x" and "y" (not necessary for app to work)
  5. Set up the connections:
    • Have the add & remove buttons send to the NSArrayController's add: and remove: actions.
    • Bind the NSTextFields values to the FunctionTables's f.slope and f.intercept key paths.
    • Bind the values of both columns of the NSTableView to FunctionTables's xs.
    • Set the value transformer for the second column to "Function Transformer"
    • Bind the NSArrayController's content array to the FunctionTable's xs key.
    • If you've got an app delegate, connect it to the File's Owner's delegate outlet.

Now build and run. You can use the add and remove buttons to add and remove rows to/from the table. You can edit the "x" and "y" column in a row (the latter is thanks to reverseTransformedValue:). You can sort by either the "x" or "y" columns. You can change the slope and intercept, though you won't notice the updates in the table unless you select the rows individually.

Advanced Topics

To fix the table view update problem, we need to propagate changes on one object's (a FunctionTransformer) properties to changes on another's (a FunctionTable) properties. We'll have the FunctionTable observe changes on its function transformer's properties and, when it FunctionTable receives a notice that any such property has changed, send a notice that the xs property has changed (which is a bit of an abuse, since xs hasn't actually changed). This is going to get a little magical, so bear with me.

An object subscribes to changes on another object using the KVO method addObserver:forKeyPath:options:context: of the other object, and unsubscribes using removeObserver:forKeyPath:. These methods just need to be called, not written. Notifications are handled by a observeValueForKeyPath:ofObject:change:context: method of the observing object, so this method needs to be written. Finally, an object can send its own notifications by calling willChangeValueForKey: and didChangeValueForKey:. Other methods exist to send notifications that only part of a collection has changed, but we won't use them here.

Our FunctionTable could handle the change subscription and unsubscription, but then it has to know which properties of the function transformer to observe, which means you couldn't change the type of the transformer. You could add methods to each concrete function transformer to subscribe and unsubscribe an observer:

@implementation LinearTransformer
...
-(void)addObserver:(NSObject *)observer 
                   options:(NSKeyValueObservingOptions)options 
                   context:(void *)context 
{
    [self addObserver:observer
           forKeyPath:@"slope"
              options:options
              context:context];
    [self addObserver:observer
           forKeyPath:@"intercept"
              options:options
              context:context];
}
-(void)removeObserver:(id)observer {
    [self removeObserver:observer forKeyPath:@"slope"];
    [self removeObserver:observer forKeyPath:@"intercept"];
}
@end

However, this will require a fair bit of code repetition in each method and across each concrete function transformer. Using some magic (reflection and closures, or as they're called in Objective-C, blocks ([2])), we can add the methods (named addObserver:options:context: and removeObserver:, as they are functionally similar to the KVO methods for subscribing & unsubscribing) to FunctionTransformer, or even to NSObject. Since observing all properties on an object isn't just limited to FunctionTransformers, we'll add the methods to NSObject. For this to work, you'll need either OS X 10.6 or PLBlocks and OS X 10.5.

Let's start from the top down, with the changes to FunctionTable. There's now new subtasks when setting the function transformer: unsubscribing from changes to the old transformer and subscribing to changes to the new one. The setF: method thus needs to be updated to make use of NSObject's new methods, which will be defined in a header named "NSObject_Properties.h". Note we don't need to worry about the implementation of these methods yet. We can use them here, having faith that we will write suitable implementations later. FunctionTable also needs a new method to handle change notifications (the observeValueForKeyPath:ofObject:change:context: referred to earlier).

#import "NSObject_Properties.h"
@interface FunctionTable
...
-(void)setF:(FunctionTransformer *)func {
    if (func != f) {
        [f removeObserver:self];
        [f release];
        f = [func retain];
        [f addObserver:self
               options:NSKeyValueObservingOptionPrior
               context:NULL];
        [NSValueTransformer setValueTransformer:f forName:kFunctionTransformer];
    }
}


- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (object == f) {
        if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
            [self willChangeValueForKey:@"xs"];
        } else {
            [self didChangeValueForKey:@"xs"];
        }
    }
}

Next, we write the new methods on NSObject. The methods to subscribe or unsubscribe from changes will loop over the object's properties, so we'll want a helper method, forEachProperty, to perform the loop. This helper method will take a block that it calls on each property. The subscription and unsubscription methods will simply call forEachProperty, passing a block that calls the standard KVO methods (addObserver:forKeyPath:options:context: and removeObserver:forKeyPath:) on each property to add or remove subscriptions.

//NSObject_Properties.h
#import <Cocoa/Cocoa.h>
#import <objc/runtime.h>

@interface NSObject (Properties)
typedef void (^PropertyBlock)(objc_property_t prop, NSString *name);
-(void)forEachProperty:(PropertyBlock)block;

-(void)addObserver:(id)observer options:(NSKeyValueObservingOptions)options context:(void *)context;
-(void)removeObserver:(id)observer;
@end

// NSObject_Properties.m:
...
@implementation NSObject (Properties)
-(void)forEachProperty:(PropertyBlock)block {
    unsigned int propCount, i;
    objc_property_t * props = class_copyPropertyList([self class], &propCount);
    NSString *name;

    for (i=0; i < propCount; ++i) {
        name = [[NSString alloc] 
                    initWithCString:property_getName(props[i]) 
                    encoding:NSUTF8StringEncoding];
        block(props[i], name);
        [name release];
    }

    free(props);
}

-(void)addObserver:(NSObject *)observer 
           options:(NSKeyValueObservingOptions)options 
           context:(void *)context 
{
    [self forEachProperty:^(objc_property_t prop, NSString *name) {
        [self addObserver:observer
               forKeyPath:name
                  options:options
                  context:context];

    }];
}
-(void)removeObserver:(id)observer {
    [self forEachProperty:^(objc_property_t prop, NSString *name) {
        [self removeObserver:observer forKeyPath:name];
    }]; 
}
@end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文