문제

나는 Cocoa의 KVC/KVO와 바인딩으로 기발한 행동을보고 있습니다. 나는있다 NSArrayController 객체, '내용'이 NSMutableArray, 그리고 나는 관찰자로 등록 된 컨트롤러가 있습니다. arrangedObjects 에 속성 NSArrayController. 이 설정을 사용하면 배열이 수정 될 때마다 KVO 알림을받을 것으로 예상됩니다. 그러나 KVO 알림은 한 번만 전송되는 것으로 보입니다. 배열이 처음 수정되었습니다.

문제를 설명하기 위해 Xcode에 새로운 "Cocoa Application"프로젝트를 설정했습니다. 내 코드는 다음과 같습니다.

BindingTesterAppDelegate.h

#import <Cocoa/Cocoa.h>

@interface BindingTesterAppDelegate : NSObject <NSApplicationDelegate>
{
    NSWindow * window;
    NSArrayController * arrayController;
    NSMutableArray * mutableArray;
}
@property (assign) IBOutlet NSWindow * window;
@property (retain) NSArrayController * arrayController;
@property (retain) NSMutableArray * mutableArray;
- (void)changeArray:(id)sender;
@end

BindingTesterAppDelegate.m

#import "BindingTesterAppDelegate.h"

@implementation BindingTesterAppDelegate

@synthesize window;
@synthesize arrayController;
@synthesize mutableArray;

- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
    NSLog(@"load");

    // create the array controller and the mutable array:
    [self setArrayController:[[[NSArrayController alloc] init] autorelease]];
    [self setMutableArray:[NSMutableArray arrayWithCapacity:0]];

    // bind the arrayController to the array
    [arrayController bind:@"content" // see update
                 toObject:self
              withKeyPath:@"mutableArray"
                  options:0];

    // set up an observer for arrangedObjects
    [arrayController addObserver:self
                      forKeyPath:@"arrangedObjects"
                         options:0
                         context:nil];

    // add a button to trigger events
    NSButton * button = [[NSButton alloc]
                         initWithFrame:NSMakeRect(10, 10, 100, 30)];
    [[window contentView] addSubview:button];
    [button setTitle:@"change array"];
    [button setTarget:self];
    [button setAction:@selector(changeArray:)];
    [button release];

    NSLog(@"run");
}

- (void)changeArray:(id)sender
{
    // modify the array (being sure to post KVO notifications):
    [self willChangeValueForKey:@"mutableArray"];
    [mutableArray addObject:[NSString stringWithString:@"something"]];
    NSLog(@"changed the array: count = %d", [mutableArray count]);
    [self didChangeValueForKey:@"mutableArray"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    NSLog(@"%@ changed!", keyPath);
}

- (void)applicationWillTerminate:(NSNotification *)notification
{
    NSLog(@"stop");
    [self setMutableArray:nil];
    [self setArrayController:nil];
    NSLog(@"done");
}

@end

그리고 다음은 출력이 있습니다.

load
run
changed the array: count = 1
arrangedObjects changed!
changed the array: count = 2
changed the array: count = 3
changed the array: count = 4
changed the array: count = 5
stop
arrangedObjects changed!
done

보시다시피, KVO 알림은 처음으로 만 전송됩니다 (그리고 응용 프로그램이 종료 될 때 다시 한 번). 왜 이것이 사실입니까?

업데이트:

감사합니다 오키 내가 contentArrayNSArrayController, 그것뿐만 아니라 content. 위에 게시 된 코드는이 변경이 이루어 지 자마자 작동합니다.

// bind the arrayController to the array
[arrayController bind:@"contentArray" // <-- the change was made here
             toObject:self
          withKeyPath:@"mutableArray"
              options:0];
도움이 되었습니까?

해결책

먼저 ContentArray (내용이 아님)에 바인딩해야합니다.

    [arrayController bind:@"contentArray"
             toObject:self
          withKeyPath:@"mutableArray"
              options:0];

그런 다음 간단한 방법은 ArrayController를 사용하여 배열을 수정하는 것입니다.

- (void)changeArray:(id)sender
{
    // modify the array (being sure to post KVO notifications):
    [arrayController addObject:@"something"];
    NSLog(@"changed the array: count = %d", [mutableArray count]);
}

(실제 시나리오에서는 버튼 작업이 -adobject를 호출하는 것만 원할 것입니다. :)

사용 -[NSMutableAreRay addObject] 사용해도 컨트롤러에 자동으로 알리지 않습니다. MutableArray에서 WillChange/DidChange를 수동으로 사용 하여이 작업을 수행하려고했습니다. 배열 자체가 변경되지 않았기 때문에 작동하지 않습니다. 즉, KVO 시스템이 변경 전후에 MutableARRay를 쿼리하는 경우 여전히 동일한 주소를 갖습니다.

사용하려면 -[NSMutableArray addObject], 당신은 배열로 willchange/didchange를 할 수 있습니다.

- (void)changeArray:(id)sender
{
    // modify the array (being sure to post KVO notifications):
    [arrayController willChangeValueForKey:@"arrangedObjects"];
    [mutableArray addObject:@"something"];
    NSLog(@"changed the array: count = %d", [mutableArray count]);
    [arrayController didChangeValueForKey:@"arrangedObjects"];
}

동일한 효과를주는 저렴한 키가있을 수 있습니다. 선택권이있는 경우 컨트롤러를 통해 작업하고 알림을 기본 시스템으로 남겨 두는 것이 좋습니다.

다른 팁

전체 값 KVO 알림을 명시 적으로 게시하는 것보다 훨씬 더 나은 방법은 구현하는 것입니다. 어레이 액세서 그리고 그것들을 사용하십시오. 그런 다음 KVO는 알림을 무료로 게시합니다.

그런 식으로, 이것 대신 :

[self willChangeValueForKey:@"things"];
[_things addObject:[NSString stringWithString:@"something"]];
[self didChangeValueForKey:@"things"];

당신은 이것을 할 것입니다 :

[self insertObject:[NSString stringWithString:@"something"] inThingsAtIndex:[self countOfThings]];

KVO는 귀하에게 변경 알림을 게시 할뿐만 아니라 전체 배열 변경보다는 배열 삽입 변경이되는보다 구체적인 알림이 될 것입니다.

나는 보통 추가합니다 addThingsObject: 내가 할 수 있도록 위의 방법을 수행하는 방법 :

[self addThingsObject:[NSString stringWithString:@"something"]];

주목하십시오 add<Key>Object: 현재 배열 속성에 대한 KVC- 인식 된 선택기 형식이 아님 (Set 속성 만). insertObject:in<Key>AtIndex: IS, 따라서 전자의 구현 (그렇게하기로 선택한 경우) ~ 해야 하다 후자를 사용하십시오.

오, 나는이 솔루션을 위해 오랜 시간을 찾고 있었다! 모두에게 감사합니다! 아이디어를 얻고 연주 한 후, 나는 또 다른 매우 멋진 방법을 찾았습니다.

다음과 같은 객체 큐브 프레임이 있다고 가정합니다.

@interface CubeFrames : NSObject {
NSInteger   number;
NSInteger   loops;
}

내 배열에는 큐브 프레임의 객체가 포함되어 있으며 ObjectController가 (MVC)를 관리하고 테이블 뷰에 표시됩니다. 바인딩은 공통적 인 방식으로 수행됩니다. ObjectController의 "컨텐츠 배열"이 내 배열에 바인딩됩니다. 중요 : ObjectController의 "클래스 이름"을 클래스 큐브 프레임으로 설정

내 AppDelegate에서 이와 같은 관찰자를 추가하면 :

-(void)awakeFromNib {

//
// register ovbserver for array changes :
// the observer will observe  each item of the array when it changes:
//      + adding a cubFrames object
//      + deleting a cubFrames object
//      + changing values of loops or number in the tableview
[dataArrayCtrl addObserver:self forKeyPath:@"arrangedObjects.loops" options:0 context:nil];
[dataArrayCtrl addObserver:self forKeyPath:@"arrangedObjects.number" options:0 context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                  ofObject:(id)object
                    change:(NSDictionary *)change
                   context:(void *)context
{
    NSLog(@"%@ changed!", keyPath);
}

이제 저는 모든 변경 사항을 포착합니다. 행 추가 및 삭제, 루프 또는 번호 변경 :-)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top