• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 42  文章 - 3  trackbacks - 0
            <2012年6月>
            272829303112
            3456789
            10111213141516
            17181920212223
            24252627282930
            1234567

            常用鏈接

            留言簿(2)

            隨筆檔案

            文章檔案

            網(wǎng)頁收藏

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            How Cocoa Bindings Work (via KVC and KVO)

            Cocoa bindings can be a little confusing, especially to newcomers. Once you have an understanding of the underlying concepts, bindings aren’t too hard. In this article, I’m going to explain the concepts behind bindings from the ground up; first explaining Key-Value Coding (KVC), then Key-Value Observing (KVO), and finally explaining how Cocoa bindings are built on top of KVC and KVO.

             

            Key-Value Coding (KVC)

            The first concept you need to understand is Key-Value Coding (KVC), as KVO and bindings are built on top of it.

             

            Objects have certain "properties". For example, a Person object may have an name property and an address property. In KVC parlance, the Person object has a value for the name key, and for the address key. "Keys" are just strings, and "values" can be any type of object[1]. At it’s most fundamental level, KVC is just two methods: a method to change the value for a given key (mutator), and a method to retrieve the value for a given key (accessor). Here is an example:

             

            void ChangeName(Person* p, NSString* newName)

            {

                //using the KVC accessor (getter) method

                NSString* originalName = [p valueForKey:@"name"];

             

                //using the KVC mutator (setter) method.

                [p setValue:newName forKey:@"name"];

             

                NSLog(@"Changed %@'s name to: %@", originalName, newName);

            }

            Now let’s say the Person object has a third key: a spouse key. The value for the spouse key is another Person object. KVC allows you to do things like this:

             

            void LogMarriage(Person* p)

            {

                //just using the accessor again, same as example above

                NSString* personsName = [p valueForKey:@"name"];

             

                //this line is different, because it is using

                //a "key path" instead of a normal "key"

                NSString* spousesName = [p valueForKeyPath:@"spouse.name"];

             

                NSLog(@"%@ is happily married to %@", personsName, spousesName);

            }

            Cocoa makes a distinction between "keys" and "key paths". A "key" allows you to get a value on an object. A "key path" allows you to chain multiple keys together, separated by dots. For example, this…

             

            [p valueForKeyPath:@"spouse.name"];

            is exactly the same as this…

             

            [[p valueForKey:@"spouse"] valueForKey:@"name"];

            That’s all you need to know about KVC for now.

             

            Let’s move on to KVO.

             

            Key-Value Observing (KVO)

            Key-Value Observing (KVO) is built on top of KVC. It allows you to observe (i.e. watch) a KVC key path on an object to see when the value changes. For example, let’s write some code that watches to see if a person’s address changes. There are three methods of interest in the following code:

             

            watchPersonForChangeOfAddress: begins the observing

            observeValueForKeyPath:ofObject:change:context: is called every time there is a change in the value of the observed key path

            dealloc stops the observing

            static NSString* const KVO_CONTEXT_ADDRESS_CHANGED = @"KVO_CONTEXT_ADDRESS_CHANGED"

             

            @implementation PersonWatcher

             

            -(void) watchPersonForChangeOfAddress:(Person*)p;

            {

                //this begins the observing

                [p addObserver:self

                    forKeyPath:@"address"

                       options:0

                       context:KVO_CONTEXT_ADDRESS_CHANGED];

             

                //keep a record of all the people being observed,

                //because we need to stop observing them in dealloc

                [m_observedPeople addObject:p];

            }

             

            //whenever an observed key path changes, this method will be called

            - (void)observeValueForKeyPath:(NSString *)keyPath

                                  ofObject:(id)object

                                    change:(NSDictionary *)change

                                   context:(void *)context;

            {

                //use the context to make sure this is a change in the address,

                //because we may also be observing other things

                if(context == KVO_CONTEXT_ADDRESS_CHANGED){

                    NSString* name = [object valueForKey:@"name"];

                    NSString* address = [object valueForKey:@"address"];

                    NSLog(@"%@ has a new address: %@", name, address);

                }       

            }

             

            -(void) dealloc;

            {

                //must stop observing everything before this object is

                //deallocated, otherwise it will cause crashes

                for(Person* p in m_observedPeople){

                    [p removeObserver:self forKeyPath:@"address"];

                }

                [m_observedPeople release]; m_observedPeople = nil;

                [super dealloc];

            }

             

            -(id) init;

            {

                if(self = [super init]){

                    m_observedPeople = [NSMutableArray new];

                }

                return self;

            }

             

            @end

            This is all that KVO does. It allows you to observe a key path on an object to get notified whenever the value changes.

             

            Cocoa Bindings

            Now that you understand the concepts behind KVC and KVO, Cocoa bindings won’t be too mysterious.

             

            Cocoa bindings allow you to synchronise two key paths[2] so they have the same value. When one key path is updated, so is the other one.

             

            For example, let’s say you have a Person object and an NSTextField to edit the person’s address. We know that every Person object has an address key, and thanks to the Cocoa Bindings Reference, we also know that every NSTextField object has a value key that works with bindings. What we want is for those two key paths to be synchronised (i.e. bound). This means that if the user types in the NSTextField, it automatically updates the address on the Person object. Also, if we programmatically change the the address of the Person object, we want it to automatically appear in the NSTextField. This can be achieved like so:

             

            void BindTextFieldToPersonsAddress(NSTextField* tf, Person* p)

            {

                //This synchronises/binds these two together:

                //The `value` key on the object `tf`

                //The `address` key on the object `p`

                [tf bind:@"value" toObject:p withKeyPath:@"address" options:nil];

            }

            What happens under the hood is that the NSTextField starts observing the address key on the Person object via KVO. If the address changes on the Person object, the NSTextField gets notified of this change, and it will update itself with the new value. In this situation, the NSTextField does something similar to this:

             

            - (void)observeValueForKeyPath:(NSString *)keyPath

                                  ofObject:(id)object

                                    change:(NSDictionary *)change

                                   context:(void *)context;

            {

                if(context == KVO_CONTEXT_VALUE_BINDING_CHANGED){

                    [self setStringValue:[object valueForKeyPath:keyPath]];

                }       

            }

            When the user starts typing into the NSTextField, the NSTextField uses KVC to update the Person object. In this situation, the NSTextField does something similar to this:

             

            - (void)insertText:(id)aString;

            {

                NSString* newValue = [[self stringValue] stringByAppendingString:aString];

                [self setStringValue:newValue];

             

                //if "value" is bound, then propagate the change to the bound object

                if([self infoForBinding:@"value"]){

                    id boundObj = ...; //omitted for brevity

                    NSString* boundKeyPath = ...; //omitted for brevity

                    [boundObj setValue:newValue forKeyPath:boundKeyPath];

                }

            }

            For a more complete look at how views propagate changes back to the bound object, see my article: Implementing Your Own Cocoa Bindings.

             

            Conclusion

            That’s that basics of how KVC, KVO and bindings work. The views use KVC to update the model, and they use KVO to watch for changes in the model. I have left out quite a bit of detail in order to keep the article short and simple, but hopefully it has given you a firm grasp of the concepts and principles.

             

            Footnotes

            [1] KVC values can also be primitives such as BOOL or int, because the KVC accessor and mutator methods will perform auto-boxing. For example, a BOOL value will be auto-boxed into an NSNumber*.

            [2] When I say that bindings synchronise two key paths, that’s not technically correct. It actually synchronises a "binding" and a key path. A "binding" is a string just like a key path but it’s not guaranteed to be KVC compatible, although it can be. Notice that the example code uses @"address" as a key path but never uses @"value" as a key path. This is because @"value" is a binding, and it might not be a valid key path.

             

            posted on 2012-07-16 16:27 鷹擊長空 閱讀(295) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            青青久久精品国产免费看| 久久久无码精品亚洲日韩按摩| 狠狠色丁香久久婷婷综合五月| 久久发布国产伦子伦精品| 国产精品狼人久久久久影院| 久久精品国产色蜜蜜麻豆| 久久精品国产AV一区二区三区| 精品综合久久久久久888蜜芽| 国产精品美女久久久久AV福利| 午夜视频久久久久一区 | 亚洲精品美女久久久久99小说| 性高湖久久久久久久久| 久久www免费人成精品香蕉| 一本色道久久88精品综合| 精品无码久久久久久国产| 久久精品亚洲精品国产色婷| 日本欧美国产精品第一页久久| 国产综合久久久久| 人人妻久久人人澡人人爽人人精品 | 久久久久亚洲av无码专区导航| 久久久精品视频免费观看| 狠狠色丁香久久综合五月| 狠狠色噜噜色狠狠狠综合久久| 国产精品久久久久久久午夜片 | 久久久久一区二区三区| 熟妇人妻久久中文字幕| 亚洲精品国精品久久99热| 久久综合狠狠综合久久97色| 久久国产精品久久| 久久精品国产只有精品2020| 日产精品99久久久久久| 欧洲精品久久久av无码电影 | 久久久久久综合一区中文字幕| 久久久亚洲欧洲日产国码aⅴ| 亚洲愉拍99热成人精品热久久| 2020国产成人久久精品| 热RE99久久精品国产66热| 亚洲另类欧美综合久久图片区| 久久久99精品成人片中文字幕 | 香蕉久久夜色精品国产小说| 久久国产高潮流白浆免费观看|