简体   繁体   中英

Access Objective-C Category's Property (Associated Reference) in Swift 3's Value Type

In my Objective-C class's .h file, I have created a Category for NSIndexPath like this:

@interface NSIndexPath (YVTableView)

@property (nonatomic, assign) NSInteger subRow;

@end

and in that class's .m file, I have implemented that like:

static void *SubRowObjectKey;

@implementation NSIndexPath (YVTableView)

@dynamic subRow;

- (NSInteger)subRow
{
    id subRowObj = objc_getAssociatedObject(self, SubRowObjectKey);
    return [subRowObj integerValue];
}

- (void)setSubRow:(NSInteger)subRow
{
    id subRowObj = [NSNumber numberWithInteger:subRow];
    objc_setAssociatedObject(self, SubRowObjectKey, subRowObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

Now, when I am accessing the subRow property of NSIndexPath in Swift 3 using IndexPath then its giving me error:

Value of type 'IndexPath' has no member 'subRow'

If I am trying to access it using type casting

let subIndexPath = indexPath as NSIndexPath
let subRowIndex = subIndexPath.subRow

its always returning me '0' as 'subRow' value. May be its because IndexPath is value type and NSIndexPath is reference type.

I have implemented UITableViewDelegate to my Custom Delegate using Objective-C and implemented it to Swift class but in Swift where I am implementing the Custom Delegate methods, I am facing this issue.

I also tried to use NSIndexPath instead of IndexPath in my Custom Delegate implementation (Swift code) but it was giving me the error "Type YVViewController does not conform to protocol, Candidate has non matching-type".

Here is my Custom Delegate declaration :

@protocol YVTableViewDelegate <UITableViewDelegate>

@required

- (UITableViewCell *)tableView:(SKSTableView *)tableView cellForSubRowAtIndexPath:(NSIndexPath *)indexPath;

@end

Its working perfectly fine with Swift 2.x but after migrating to Swift 3, I am facing this issue.

The subRow associated with an NSIndexPath will not be associated with an IndexPath casted from that NSIndexPath because they are not the same "object". IndexPath is a value type in Swift, defined with the struct keyword, so it cannot have an Objective-C associated object.

So even if you have set a value to subRow of an NSIndexPath in Objective-C code, the value is lost when that NSIndexPath is casted to Swift's IndexPath . That's why when you again cast the IndexPath back to NSIndexPath , the subRow value is always 0, which is the default value.

In your case, you need to declare the protocol in Swift and specify the index path parameter type as NSIndexPath .

func tableView(_ tableView: SKSTableView, cellForSubRowAt indexPath: NSIndexPath) -> UITableViewCell

You can however update your category as follows:-

- (NSInteger)subRow
{

    id myclass = [SKSTableView class];
  //  id subRowObj = objc_getAssociatedObject(self, SubRowObjectKey);
    id subRowObj = objc_getAssociatedObject(myclass, SubRowObjectKey);
    return [subRowObj integerValue];
}

- (void)setSubRow:(NSInteger)subRow
{
    id subRowObj = [NSNumber numberWithInteger:subRow];

    id myclass = [SKSTableView class];
   // objc_setAssociatedObject(self, SubRowObjectKey, subRowObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    objc_setAssociatedObject(myclass, SubRowObjectKey, subRowObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM