vl4dimir / ObjectiveMixin

Ruby-like mixin functionality for Objective-C programs.
BSD 2-Clause "Simplified" License
217 stars 17 forks source link

Problem when mixing into class with ivars #9

Closed cneuwirt closed 11 years ago

cneuwirt commented 12 years ago

I tried mixing into UiViewController or any class that has existing ivars which resulted in mismatched ivars. Since existing instance variables cannot be added after registration, the mixed in ivars will overwrite the corresponding location in the destination class. Is there a test that shows mixing into a source class with ivars into a destination class with ivars? If not I could write a failing one.

cheers, craig

scriptease commented 12 years ago

The problem is the iVar layout is hardcoded at compile time while the mixin is applied at runtime.

Have you tried associated objects?

http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html

cheers, Florian

cneuwirt commented 12 years ago

Wow, didn't mean to bug you on a Sunday, but thanks. I haven't tried Associative References, but that sounds very interesting and will be taking a look at it.

thanks again, -craig

On Oct 7, 2012, at 10:00 AM, Florian Agsteiner wrote:

The problem is the iVar layout is hardcoded at compile time while the mixin is applied at runtime.

Have you tried associated objects?

http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html

cheers, Florian

— Reply to this email directly or view it on GitHub.

scriptease commented 12 years ago

No problem, it's actually very easy

-(void)setVariable: (NSString *)myString
{
    objc_setAssociatedObject(self, @selector(variable), myString, OBJC_ASSOCIATION_RETAIN);
}

-(NSString *)variable
{
    return objc_getAssociatedObject(self, @selector(variable));
}
cneuwirt commented 12 years ago

Indeed, that should work great!!!!

cheers

On Oct 7, 2012, at 10:20 AM, Florian Agsteiner wrote:

No problem, it's actually very easy

-(void)setVariable: (NSString *)myString { objc_setAssociatedObject(self, @selector(variable), myString, OBJC_ASSOCIATION_RETAIN); }

-(NSString *)variable { return objc_getAssociatedObject(self, @selector(variable)); } — Reply to this email directly or view it on GitHub.

cneuwirt commented 12 years ago

Using your mixin support with associated references works perfectly!

can't thank you enough for that this gem. I've been wanting to add a contextual model concepts to cocoa touch to mirror the great support for view and controller hierarchies and this will get me there.

On Oct 7, 2012, at 10:20 AM, Florian Agsteiner wrote:

No problem, it's actually very easy

-(void)setVariable: (NSString *)myString { objc_setAssociatedObject(self, @selector(variable), myString, OBJC_ASSOCIATION_RETAIN); }

-(NSString *)variable { return objc_getAssociatedObject(self, @selector(variable)); } — Reply to this email directly or view it on GitHub.

scriptease commented 12 years ago

It's not mine, but i love to use it too ;-)

But i think @vl4dimir will appreciate it :-)

cneuwirt commented 12 years ago

Oops..I'll certainly let him know too.

On Oct 7, 2012, at 10:53 AM, Florian Agsteiner wrote:

It's not mine, but i love to use it too ;-)

But i think @vl4dimir will appreciate it :-)

— Reply to this email directly or view it on GitHub.

cneuwirt commented 12 years ago

Just for my understanding, is there any difference using mixins vs objective c categories?

On Oct 7, 2012, at 10:53 AM, Florian Agsteiner wrote:

It's not mine, but i love to use it too ;-)

But i think @vl4dimir will appreciate it :-)

— Reply to this email directly or view it on GitHub.

scriptease commented 12 years ago

They can ca little more:

1) The addition of ivars actually works when the class size is the same po (int)class_getInstanceSize([XYMixin class]) po (int)class_getInstanceSize([XY class])

Thats why -> notation can be difficult

2) I like the following feature, it works by creating a protocol of the host class e.g.

========================
.h
========================

@protocol  SimpleClassMethods<NSObject>
     - (id) doSomething;
@end

@interface SimpleClassMixin extends NSObject
   - (void) convenientMethodThatDoesSomething
@end

========================
.m
========================

 @interface SimpleClassMixin <SimpleClassMethods> (CategoryToAvoidWarnings)
 @end

 @implementation SimpleClassMixin

 - (void) convenientMethodThatDoesSomething{
       [self doSomething];
  }
  @end

This allows to add convenient methods without coping them and force the Host Class to implement the protocol

 @interface DemoClass extends NSObject<SimpleClassMethods> .....

3) It can be added to more than one class without coping the code to the two categories, actually it is copied but automatically ;-)

4) I would use it to add simiar code to serveral Framework classes, where you don't wont the superclass to have have the code, e.g:

LayoutScrollView extends UIScrollview and LayoutView extends UIview, but i want the code to end up in the Layout* classes not the UIView.

cneuwirt commented 12 years ago

Ah. That makes a lot if sense. Thanks for the explanation.

Sent from my iPhone

On Oct 7, 2012, at 1:48 PM, Florian Agsteiner notifications@github.com wrote:

They can ca little more:

1) The addition of ivars actually works when the class size is the same po (int)class_getInstanceSize([XYMixin class]) po (int)class_getInstanceSize([XY class])

Thats why -> notation can be difficult

I fix it by creating a protocol of the host class e.g.

.h

@protocol SimpleClassMethods

  • (id) doSomething; @end

@interface SimpleClassMixin extends NSObject

  • (void) convenientMethodThatDoesSomething @end

.m

@interface SimpleClassMixin (CategoryToAvoidWarnings) @end

@implementation SimpleClassMixin

  • (void) convenientMethodThatDoesSomething{ [self doSomething]; } @end This allows to add convenient methods without coping them and force the Host Class to implement the protocol

    @interface DemoClass extends NSObject ..... 2) It can be added to more than one class without coping the code to the two categories, actually it is copied but automatically ;-)

— Reply to this email directly or view it on GitHub.

cneuwirt commented 12 years ago

On Oct 7, 2012, at 1:48 PM, Florian Agsteiner wrote:

They can ca little more:

1) The addition of ivars actually works when the class size is the same po (int)class_getInstanceSize([XYMixin class]) po (int)class_getInstanceSize([XY class])'''

How is that possible? Doesn't that suggest adding new ivars to the destination class? Looking at the MixIn code, it only adds methods, so how can it add variables?

Thats why -> notation can be difficult

I fix it by creating a protocol of the host class e.g.

.h

@protocol SimpleClassMethods

  • (id) doSomething; @end

@interface SimpleClassMixin extends NSObject

  • (void) convenientMethodThatDoesSomething @end

.m

@interface SimpleClassMixin (CategoryToAvoidWarnings) @end

@implementation SimpleClassMixin

  • (void) convenientMethodThatDoesSomething{ [self doSomething]; } @end This allows to add convenient methods without coping them and force the Host Class to implement the protocol

    @interface DemoClass extends NSObject ..... 2) It can be added to more than one class without coping the code to the two categories, actually it is copied but automatically ;-)

— Reply to this email directly or view it on GitHub.

scriptease commented 12 years ago

I mean copiing the ivars could work sorry.

If you are interested in the subject, here is a reading list:

http://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html http://blog.mugunthkumar.com/products/introducing-my-book-ios-5-programming-pushing-the-limits/

cneuwirt commented 12 years ago

Great read. Thanks for the links.

The use of obj_setAssociatedObject in my mixin works greats. I did notice that these associations are not always getting cleaned up and most likely leaking. Not sure if its cycles, block usage or something else thats preventing this. It does clean up sometimes.

Thanks again for all your great advice on this subject

cheers

On Oct 9, 2012, at 1:15 PM, Florian Agsteiner wrote:

I mean copiing the ivars could work sorry.

If you are interested in the subject, here is a reading list:

http://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html http://blog.mugunthkumar.com/products/introducing-my-book-ios-5-programming-pushing-the-limits/

— Reply to this email directly or view it on GitHubhttps://github.com/vl4dimir/ObjectiveMixin/issues/9#issuecomment-9272873.

scriptease commented 12 years ago

They are cleaned up when the object is deallocated.

So when you have a delegate make sure to set it to OBJC_ASSOCIATION_ASIGN.

If it is a block, that contains "self" it will result in a retainCycle because the object retains the block and the block the object. To cut the cycle the best way is to use an external lifeCycle event, for example [UIViewController viewDidDisappear] or [UIView didMoveToWindow] and window == nil, etc...

There is a good tool hidden in the instrument leaks to visualize retain cycles!

Cheers, Florian

cneuwirt commented 12 years ago

Turned out there really wasn't a leak after all. I was tricked by my unit tests into believing there was due to the delayed draining of the autoreleasepool by the tests. Introducing a local @autoreleasepool eliminated the leaks.

thanks again -craig

On Oct 10, 2012, at 1:17 PM, Florian Agsteiner wrote:

They are cleaned up when the object is deallocated.

So when you have a delegate make sure to set it to OBJC_ASSOCIATION_ASIGN.

If it is a block, that contains "self" it will result in a retainCycle because the object retains the block and the block the object. To cut the cycle the best way is to use an external lifeCycle event, for example [UIViewController viewDidDisappear] or [UIView didMoveToWindow] and window == nil, etc...

There is a good tool hidden in the instrument leaks to visualize retain cycles!

Cheers, Florian

— Reply to this email directly or view it on GitHub.